线程的建立、线程池、线程状态、线程调度

由于子类重写了Thread类的run(), 当调用start()时, 直接找子类的run()方法,不是我们调用的,是虚拟机帮我们调用的,所以我们在查看源码的时候是找不到的

一、继承Thread类:

  1)最传统的方式

public class Demo2_Thread {
    public static void main(String[] args) {
        MyThread mt = new MyThread();        //4,创建Thread类的子类对象
        mt.start();                            //5,开启线程,默认调用run方法
        
        for(int i = 0; i < 1000; i++) {
            System.out.println("bb");
        }
    }
}
class MyThread extends Thread {                //1,继承Thread
    public void run() {                        //2,重写run方法
        for(int i = 0; i < 1000; i++) {        //3,将要执行的代码写在run方法中
            System.out.println("aaaaaaaaaaaa");
        }
    }
}
View Code

  2)匿名内部类 

new Thread(){                                    
    @Override                                    
    public void run(){                           
        for(int i = 0; i < 1000; i++) {          
            System.out.println("aaaaaaaaaaaaaa");
        }                                        
    }                                            
}.start();                                       
View Code

  3)Thread 是个类,不是接口,所以不能用函数式编程

二、实现Runnable接口:

  一定要知道,最后还是通过Thread对象来创建的,Thread的构造方法里面是塞一个runnable接口对象

  1)最传统的方式

public class Demo3_Thread {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();    //4,创建Runnable的子类对象
        //Runnable target = mr;    mr = 0x0011
        Thread t = new Thread(mr);            //5,将其当作参数传递给Thread的构造函数
        t.start();                            //6,开启线程
        
        for(int i = 0; i < 1000; i++) {
            System.out.println("bb");
        }
    }
}

class MyRunnable implements Runnable {        //1,定义一个类实现Runnable
    @Override
    public void run() {                        //2,重写run方法
        for(int i = 0; i < 1000; i++) {        //3,将要执行的代码写在run方法中
            System.out.println("aaaaaaaaaaaa");
        }
    }   
}
View Code

   2)匿名内部类 

new Thread(                                           
        new Runnable() {                              
            @Override                                 
            public void run() {                       
                System.out.println("asfasfasfddfsaf");
            }                                         
        }                                             
){}.start(); 
View Code

   3)lambda

new Thread(() -> System.out.println("sdbhsfsgdsgffdsgf")){}.start();
View Code

 三、实现Callable接口,这个有返回值哦

  1)最传统方式

public class Xixi {
    @Test
    public void asf() throws ExecutionException, InterruptedException {
        MyTask myTask = new MyTask();
        FutureTask<String> futureTask = new FutureTask<>(myTask);
        final Thread thread = new Thread(futureTask);
        thread.start();
        System.out.println(futureTask.get());
    }
}

class MyTask implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "哈哈这是返回值啊";
    }
}
View Code

   2)匿名内部类

public class Xixi {
    @Test
    public void asf() throws ExecutionException, InterruptedException {
        FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return "哈哈这是返回值啊";
            }
        });
        final Thread thread = new Thread(futureTask);
        thread.start();
        System.out.println(futureTask.get());
    }
}
View Code

   3)lambda

public class Xixi {
    @Test
    public void asf() throws ExecutionException, InterruptedException {
        FutureTask<String> futureTask = new FutureTask<>(()->{return "哈哈这是返回值啊";});
        final Thread thread = new Thread(futureTask);
        thread.start();
        System.out.println(futureTask.get());
    }
}
View Code

四、通过线程池的方式:

  内置的5种创建方式,不过阿里巴巴规范都不给用哦,所以大概知道就是了我觉得

1、Single Thread Executor : 只有一个线程的线程池,因此所有提交的任务是顺序执行,
代码: Executors.newSingleThreadExecutor()

2、Cached Thread Pool : 线程池里有很多线程需要同时执行,老的可用线程将被新的任务触发重新执行,如果线程超过60秒内没执行,那么将被终止并从池中删除,
代码:Executors.newCachedThreadPool()

3、Fixed Thread Pool : 拥有固定线程数的线程池,如果没有任务执行,那么线程会一直等待,
代码: Executors.newFixedThreadPool(4)
在构造函数中的参数4是线程池的大小,你可以随意设置,也可以和cpu的核数量保持一致,获取cpu的数量int cpuNums = Runtime.getRuntime().availableProcessors();

4、Scheduled Thread Pool : 用来调度即将执行的任务的线程池,
代码:Executors.newScheduledThreadPool()

5、Single Thread Scheduled Pool : 只有一个线程,用来调度执行将来的任务,代码:Executors.newSingleThreadScheduledExecutor()
View Code

  一、 execute()方法(Executor接口唯一的一个方法,里面要放一个Runnable接口的对象,这个Runnable接口有一个run()的抽象方法,run()不会有任何返回结果,所以主线程无法获得任务线程的返回值),

  二、 submit() 方法,根据有没有返回值注意区别里面的参数是什么:

  留意里面的get()方法的用处哦:一是有返回值获得返回值;二是有异常可以捕获异常;三是可以阻塞,只有三面的线程全部执行完了才可以执行下面的

public class Xixi {

   private static ExecutorService executorService = Executors.newCachedThreadPool();

    /**
     * execute()方法只有一个参数是 Runnable 接口,返回值为空
     */
    @Test
    public void testExecute() {
        executorService.execute(()->{
            System.out.println(Thread.currentThread().getName() + ":开始工作");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":结束工作");
        });
    }

    /**
     * 因为有返回值,所以submit()方法里面的参数是callable对象
     * 线程方法体最后有 return 嘛
     */
    @Test
    public void testSubmitReturn() throws ExecutionException, InterruptedException {
        Future<String> submit = executorService.submit(() -> {
            System.out.println(Thread.currentThread().getName() + ":开始工作");
            TimeUnit.SECONDS.sleep(3);
            System.out.println(Thread.currentThread().getName() + ":结束工作");
            return "当前线程名字为" + Thread.currentThread().getName();
        });
        String value = submit.get();
        System.out.println("submit()方法callable参数返回值测试:" + value);
    }

    /**
     * 因为没返回值,所以submit()方法里面的参数是runnable对象
     * 线程方法体最后有 return 嘛
     */
    @Test
    public void testSubmitNotReturn() throws ExecutionException, InterruptedException {
        Future<?> submit = executorService.submit(() -> {
            System.out.println(Thread.currentThread().getName() + "-->正在工作");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "-->工作结束");
        });
        submit.get();

    }

    @AfterEach
    public void AfterEach(){
        System.out.println("线程池关闭了");
        executorService.shutdown();
    }

}
View Code

五、线程状态:

官方版(摘自Java Core):

RUNNING: The Java specification does not call this a separate state, though. A running thread is still in the runnable state.

BLOCKED: When the thread tries to acquire an intrinsic object lock that is currently held by another thread, it becomes blocked.The thread becomes unblocked when all other threads have relinquished the lock and the thread scheduler has allowed this thread to hold it.

WAITING: When the thread waits for another thread to notify the scheduler of a condition,it enters the waiting state.This happens by calling the Object.wait or Thread.join method,or by waiting for a Lock or Condition in the java.util.concurrent library. In practice,the difference between the blocked and waiting state is not significant.

TIME_WAITING: Several methods have a timeout parameter. Calling them causes the thread to enter the timed waiting state. This state persists either until the timeout expires or the appropriate notification has been received. Methods with timeout include Thread.sleep and the timed versions of Object.wait, Thread.join, Lock.tryLock,and Condition.await.
View Code

民间版:

RUNNABLE:当需要新起一个线程来执行某个子任务时,就创建了一个线程。但是线程创建之后,不会立即进入就绪状态,因为线程的运行需要一些条件(比如内存资源,在前面的JVM内存区域划分一篇博文中知道程序计数器、Java栈、本地方法栈都是线程私有的,所以需要为线程分配一定的内存空间),只有线程运行需要的所有条件满足了,才进入就绪状态。

RUNNING:当线程进入就绪状态后,不代表立刻就能获取CPU执行时间,也许此时CPU正在执行其他的事情,因此它要等待。当得到CPU执行时间之后,线程便真正进入运行状态。

BLOCK:OTHER:sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务。但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象。

yield():调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会,但是不一定能获取到。

sleep():交出CPU使用权,不会释放锁

join():让调用join方法的线程先执行完毕,在执行其他线程,类似让救护车警车优先通过
View Code

 调度图:

obj.join():是指主线程等待子线程的终止,也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行。
(为什么要用到join()方法:在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,
  但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束)

调用obj的wait(), notify()方法前,必须获得obj锁

描述线程的状态是用一个枚举类型来描述的,严格来讲一个线程有六种状态(具体查看Thread类的源代码),分别是六个枚举值:NEW(新建状态), RUNNABLE(运行状态), BLOCKED(锁池状态),TIMED_WAITING(定时等待状态), WAITING(等待状态), TERMINATED(终止状态),只不过人们平时理解的时候经常会增加阻塞状态,可运行状态,还有挂起状态。
 

如果是双核系统,那么同一时间点会有 2 条线程处于 Running 状态但是,当线程数大于处理器数时,依然会是多条线程在同一个 CPU 上轮换执行

听说是经典例子

/*
 * 但有一点需要注意的是notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后,
 * JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。
 * 打印出  10A、10B、10C、9A、9B、9C、8A、8B、8C、7A、7B、7C、6A、6B、6C、5A、5B、5C、4A、4B、4C、3A、3B、3C、2A、2B、2C、1A、1B、1C、
 */
public class Main implements Runnable {     
    
    private String name;     
    private Object prev;    // c a b
    private Object self;    // a b c 
    
    private Main(String name, Object prev, Object self) {     
        this.name = name;     
        this.prev = prev;     
        this.self = self;     
    }     
    
    @Override    
    public void run() {     
        int count = 10;     
        while (count > 0) {   
            System.out.print(count);
            // (1)线程pa获取了c对象锁,此时pb,pc线程处于BLOCKED:SYNCHRONIZED状态 
            // (7)线程pb进来,获取了a对象锁,此时pa线程处于对象c的等待队列,pc线程处于BLOCKED:SYNCHRONIZED状态
            // (13)线程pc进来,获取了b对象锁(pa在对象c的等待队列,pb在对象a的等待队列)
            // (19)只有线程pa不在等待队列(此刻pb在对象a的等待队列,pc在对象b的等待队列)
            synchronized (prev) {   
                // (2)线程pa获取了a对象锁
                // (8)线程pb获取b对象锁
                // (14)线程pc获取c对象锁
                // (20).......
                synchronized (self) {     
                    // (3)打印出A
                    // (9)打印出B
                    // (15)打印出C
                    System.out.print(name+"、"); 
                    // (4)count变9
                    // (10)count变8
                    // (16)count变7
                    count--;    
                    // (5)线程pa调用了对象a的notify()方法,唤醒从对象a的等待队列其中一个的线程(这个时候对象a等待队列实际上是没有线程的)到BLOCKED:SYNCHRONIZED(此时c对象锁还被线程pa控制住)
                    // (11)线程pb调用了对象b的notify()方法,唤醒从对象b的等待队列其中一个的线程(这个时候对象b等待队列上实际上是没有线程的,对象c等待队列上的线程只有pa)到BLOCKED:SYNCHRONIZED(此时a对象锁还被pb控制住)
                    // (17)线程pc调用了对象c的notify()方法,唤醒从对象c的等待队列其中一个的线程(pa在对象c的等待队列,pb在对象a的等待队列)(所以对象c的等待队列的线程pa被唤醒了)
                    // (21)线程pa调用了对象a的notify()方法,唤醒从对象a的等待队列其中一个的线程(唤醒线程pb)(此时线程pc在对象b的等待队列)
                    self.notify();     
                }  // self变量对象锁被释放   
                try {     
                    // (6)线程pa调用了c对象的wait方法,pa释放c对象锁,并进入对象c的等待队列中(此时线程pb,bc争夺c对象锁了)(一定一定要知道,synchronized(){}语句块执行结束,对象锁才释放的)
                    // (12)线程pb调用了对象a的wait方法,pb释放a对象锁,并进入a对象锁的等待队列(pa还在对象c的等待队列,此刻pb在对象a的等待队列)
                    // (18)线程pc释放b对象锁(此刻pb在对象a的等待队列,pc在对象b的等待队列)(所以对象c的等待队列的线程pa被唤醒了,17步被释放了)
                    // (22)线程pa释放锁c,进入对象c的等待队列(线程pa在对象c的等待队列中,pc在b的等待队列中,在a等待队列的线程pb在21步被释放了)
                    prev.wait(); 
                } catch (InterruptedException e) {     
                    e.printStackTrace();     
                }   
            } // prev  变量对象锁被释放
        }     
    }     
    
    public static void main(String[] args) throws Exception {     
        Object a = new Object();     
        Object b = new Object();     
        Object c = new Object();     
        Main pa = new Main("A", c, a);     
        Main pb = new Main("B", a, b);     
        Main pc = new Main("C", b, c);     
             
             
        new Thread(pa).start();  
        Thread.sleep(100);  //确保按顺序A、B、C执行  
        new Thread(pb).start();  
        Thread.sleep(100);    
        new Thread(pc).start();     
        Thread.sleep(100);    
   }     
}
View Code

 六、叼线程池:

 1、一开始new一个线程池的时候,即使工作队列里面有任务,线程池也不会创建线程的,也及时说里面的线程数为0,看,里面不打印出任何东西

public static void main(String[] args) throws InterruptedException {
        LinkedBlockingDeque<Runnable> linkedBlockingDeque = new LinkedBlockingDeque<>();
        for (int i = 0; i < 100; i++) {
            linkedBlockingDeque.put( () -> System.out.println(Thread.currentThread().getName()) );
        }
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20, 10,TimeUnit.SECONDS, linkedBlockingDeque);
}
View Code

2、除非调用:threadPoolExecutor.prestartAllCoreThreads();

public static void main(String[] args) throws InterruptedException {
        LinkedBlockingDeque<Runnable> linkedBlockingDeque = new LinkedBlockingDeque<>();
        for (int i = 0; i < 100; i++) {
            linkedBlockingDeque.put( () -> System.out.println(Thread.currentThread().getName()) );
        }
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20, 10,TimeUnit.SECONDS, linkedBlockingDeque);
        threadPoolExecutor.prestartAllCoreThreads();
}
View Code

3、关系,记得的调用任务数哦

public static void main(String[] args) throws InterruptedException {
        LinkedBlockingDeque<Runnable> linkedBlockingDeque = new LinkedBlockingDeque<>(15);
        System.out.println(linkedBlockingDeque.size());
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 30, 1000,TimeUnit.SECONDS, linkedBlockingDeque);
        threadPoolExecutor.prestartAllCoreThreads();
        System.out.println(threadPoolExecutor.getActiveCount());
        for (int i = 0; i < 46; i++) {
            threadPoolExecutor.submit(() -> {
                try {
                    TimeUnit.SECONDS.sleep(6);
                    System.out.println("getActiveCount: " + threadPoolExecutor.getActiveCount());
                    System.out.println("linkedBlockingDeque.size(): " + linkedBlockingDeque.size());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        threadPoolExecutor.shutdown();
}
View Code

 参考:

1)Java并发编程:Thread类的使用

原文地址:https://www.cnblogs.com/ericguoxiaofeng/p/8547124.html