zoukankan      html  css  js  c++  java
  • Java并发基础(上)——Thread

            并发编程可以使我们将程序划分为多个分离的,独立运行的任务。通过多线程机制,这些独立任务都将由执行线程来驱动。在使用线程时,CPU将轮流给每个任务分配占用时间,每个任务都觉得自己在占用CPU,但实际上CPU时间是划分为片段分配给了所有任务。

    定义任务

    继承Thread类

          我们可以继承Thread类,并重写run方法。

    public class SimpleThread extends Thread {
        @Override
        public void run(){
            for(int i = 0; i < 5; i++){
                System.out.println(getName() + i);
            }
        }
        
        public static void main(String[] args) {
            SimpleThread thread = new SimpleThread();
            thread.start();
            System.out.println(Thread.currentThread().getName());
        }
    }

    实现Runnable接口

          当然,我们还可以实现Runnable接口,并实现run接口,然后提交给Thread实例。

    public class Task implements Runnable{
        public void run(){
            for(int i = 0; i < 5; i++){
                System.out.println(Thread.currentThread().getName() + i);
            }
        }
        public static void main(String[] args) {
            Thread thread = new Thread(new Task());
            thread.start();
            System.out.println(Thread.currentThread().getName());
        }
    }

    Callable与Future

           我们知道run方法是没有返回值的,也就意味着任务完成后无法获取结果,所以我们需要Callable接口来帮助我们返回任务结果,它和Runnable接口很相似,所以我们也需要实现Callable接口的call方法。而Future接口则用来获取异步计算结果的,我们对执行结果获取,取消,或者判断是否完成。但是Callable接口并没有继承Runnable,所以并不能直接提交给Thread实例,所以我们还需要FutureTask类,它同时实现了Runnable接口和Callable接口,我们可以用FutureTask包装Callable对象,再提交给Thread实例。

    import java.util.concurrent.*;
    
    public class TaskWithResult implements Callable<Integer> {
        public Integer call(){
            int total = 0;
            for(int i = 0; i < 100; i++){
                total += i;
            }
            return total;
        }
    
        public static void main(String[] args) throws InterruptedException, ExecutionException{
            RunnableFuture<Integer> task = new FutureTask<Integer>(new TaskWithResult());
            Thread thread = new Thread(task);
            thread.start();
            while (!task.isDone()){
                System.out.println(task.get().toString());
            }
        }
    }

     后台线程

           后台线程,也叫守护线程,是指程序在运行的时候在后台提供一种通用服务的线程,并且这种线程并不属于程序中不可或缺的部分。所以,当所有非后台线程结束时,后台线程也会被结束,不管后台线程是否完成。而且,后台线程的子线程也是后台线程。

    public class Daemon implements Runnable {
        @Override
        public void run() {
            for(int i = 0; i < 10; i++){
                try{
                    Thread.sleep(1000);
                    System.out.println(i);
                }catch (InterruptedException e){
                    System.out.println(e.getMessage());
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException{
            Thread thread = new Thread(new Daemon());
            thread.setDaemon(true);
            thread.start();
            Thread.sleep(1000 * 5);
            System.out.println(Thread.currentThread().getName());
        }
    }

            这里要注意setDaemon方法必须要在start之前调用,才能将其设置为后台线程。

    线程的生命周期

    新建(new)

           当线程被创建时,它只会短暂地处于这种状态。此时它已经分配了必须的系统资源,并执行了初始化。此刻线程已经有资格获得CPU时间了,之后调度器将把这个线程转变为可运行状态或阻塞状态。

    就绪(Runnable)

           在这种状态下,只要调度器把时间片分配给线程,线程就可以运行。也就是说,在任意时刻,线程可以运行也可以不运行。只要调度器能分配时间片给线程,它就可以运行,这不同于死亡和阻塞状态。

    阻塞(Blocked)

           线程能够运行,但有某个条件阻止它的运行。当线程处于阻塞状态时,调度器将忽略线程,不会分配给线程任何CPU时间。直到线程重新进入了就绪状态,它才有可能执行操作。

           一个任务进入阻塞状态,可能有以下原因:

           (1)通过调用sleep()使任务进入休眠状态,在这种情况下,任务在指定时间内不会运行。

           (2)通过调用wait()使线程挂起。直到线程得到了notify()或notifyAll()消息,线程才会进入就绪状态。

           (3)任务在等待某个输入/输出完成。

           (4)任务试图在某个对象上调用其同步控制方法,但是对象锁不可用,因为另外一个任务已经获取了这个锁。

    死亡(Dead)

           处于死亡或终止状态的线程将不再是可调度的,并且再也不会得到CPU时间,它的任务已结束,或不再是可运行的。任务死亡的通常方式是从run()方法返回,但是任务的线程还可以被中断。

    线程控制

    线程优先级

           线程的优先级将该线程的重要性传递给调度器。尽管CPU处理现有线程集的顺序是不确定的,但是调度器将倾向于让优先级最高的线程先执行。当然,这并不意味着优先权较低的线程将得不到执行(优先级不会导致死锁)。我们可以用getPriority()来读取现有线程的优先级,通过setPriority()来修改它。尽管JDK有10个优先级,但是它与多数操作系统都不能映射的很好。所以设置优先级时,一般使用MAX_PRIORITY、NORM_PRIORITY、MIN_PRIORITY三种级别。

    休眠(Sleep)

           在线程执行的过程中调用sleep方法让其休眠(也就是进入阻塞状态)。sleep会抛出InterruptedException。

    public class Sleep {
        public static void main(String[] args) {
            System.out.println("休眠开始");
            try{
                Thread.sleep(1000 * 2);
            }catch (InterruptedException e){
                System.out.println(e.getMessage());
            }
            System.out.println("休眠结束");
        }
    }

    让步(Yield)

          如果知道已经完成了run方法的循环的一次迭代过程中所需的工作,就可以给线程调度机制一个暗示:你的工作已经做的差不多了,可以让别的线程使用CPU了,这个

    暗示将通过调用yeild作出,当然,这只是一种建议,没有任何机制保证它将会采纳。当线程切换出去后,只有优先级与当前线程相同,或优先级比当前线程更高的处于就绪的线程才会获得执行机会,因此完全有可能线程转入就绪后,调度器又将其调度出来重新执行。

    Join
       join可以一个让线程等待另一个线程执行完成,调用线程将被阻塞,直到被join的线程执行完成

    public class Task implements Runnable{
        public void run(){
            for(int i = 0; i < 5; i++){
                System.out.println(Thread.currentThread().getName() + i);
            }
            try{
                Thread.sleep(1000);
            }catch (InterruptedException e){
                System.out.println(e.getMessage());
            }
        }
        public static void main(String[] args) throws Exception{
            Thread thread = new Thread(new Task());
            thread.start();
            thread.join();
            System.out.println(Thread.currentThread().getName());
        }
    }
  • 相关阅读:
    Lua Coroutine详解
    Lua IO库详解
    vue 组件基本使用
    js 发送异步请求
    小程序保存图片到相册
    js 学习四 对象应用 吃货游戏
    js 学习三 Array
    js 学习二 字符串常用方法
    js 学习一 猜数字游戏
    phpmyadmin导入大容量.sql文件
  • 原文地址:https://www.cnblogs.com/skyshalo/p/7445257.html
Copyright © 2011-2022 走看看