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

 


四、通过线程池的方式:

  一、 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类的使用

posted @ 2019-03-27 11:10  天马行空郭  阅读(329)  评论(0编辑  收藏  举报