并发编程 -- join和sleep
Posted wx5add7776993de
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并发编程 -- join和sleep相关的知识,希望对你有一定的参考价值。
在看并发编程这本书时,看到join()方法,似曾相识的感觉,但是一时间又想不出具体的作用,觉得还是写写代码记录下,加深记忆号
先看一个demo
public class JoinAndSleepDemo
private static Thread thread1 = new Thread(new Runnable()
@Override
public void run()
System.out.println("子线1程在执行----------1111111111");
);
private static Thread thread2 = new Thread(new Runnable()
@Override
public void run()
System.out.println("子线程2在执行----------2222222222");
);
public static void main(String[] args) throws InterruptedException
//主线程
System.out.println("---------主线程------"+Thread.currentThread().getName());
System.out.println("子线程执行");
//--------子线程执行
thread1.start();
// thread1.join();
thread2.start();
//thread2.join();
//主线程希望等待子线程执行完再执行
System.out.println("主线程还在执行");
现在先不给出正确的结果,先猜想这段代码的输出会是什么样,注意,join方法都被注释掉
猜想一:
---------主线程------main
子线程执行
子线1程在执行----------1111111111
子线程2在执行----------2222222222
主线程还在执行
显然,这种猜想跟程序执行顺序差不多,思路基本上可以确定是,主线程 – 子线程 – 主线程的顺序,也就是主线程是在子线程执行完时还在执行,主线程执行时间最长
猜想二:
---------主线程------main
子线程执行
主线程还在执行
子线1程在执行----------1111111111
子线程2在执行----------2222222222
这种猜想就是主线程的执行最早结束,不等待子线程执行完就已经结束了
实际上正确的结果也就是猜想二,对于这种情况,如果有时候我们在一些业务场景中主线程希望等待子线程执行完成,并且获取子线程执行结果,那么我们该如何处理呢?
join()
还是上面那个demo,这次我把join()方法取消注释
public static void main(String[] args) throws InterruptedException
//主线程
System.out.println("---------主线程------"+Thread.currentThread().getName());
System.out.println("子线程执行");
//--------子线程执行
thread1.start();
thread1.join();
thread2.start();
thread2.join();
//主线程希望等待子线程执行完再执行
System.out.println("主线程还在执行");
那么会得到什么样的结果呢?
---------主线程------main
子线程执行
子线1程在执行----------1111111111
子线程2在执行----------2222222222
主线程还在执行
此时这种情况,主线程就会等待执行了join方法的两个子线程执行完。
表面看似这样,可实际原理是什么呢?
我们先观察线程状态在执行join方法前后会方法什么变化?
public static void main(String[] args) throws InterruptedException
//主线程
System.out.println("主线程状态---------->"+Thread.currentThread().getState());
System.out.println("---------主线程------"+Thread.currentThread().getName());
System.out.println("主线程状态---------->"+Thread.currentThread().getState());
System.out.println("子线程执行");
//--------子线程执行
thread1.start();
System.out.println(Thread.currentThread().getName()+"线程状态---------->"+Thread.currentThread().getState());
System.out.println("线程111111111状态-----------》"+thread1.getState());
thread1.join();
System.out.println("线程111111111状态-----------》"+thread1.getState());
System.out.println(Thread.currentThread().getName()+"线程状态---------->"+Thread.currentThread().getState());
thread2.start();
System.out.println("线程222222222状态-----------》"+thread2.getState());
thread2.join();
System.out.println("线程222222222状态-----------》"+thread2.getState());
System.out.println(Thread.currentThread().getName()+"线程状态---------->"+Thread.currentThread().getState());
//主线程希望等待子线程执行完再执行
System.out.println("主线程还在执行");
System.out.println(Thread.currentThread().getName()+"线程状态---------->"+Thread.currentThread().getState());
变化结果:
主线程状态---------->RUNNABLE
---------主线程------main
主线程状态---------->RUNNABLE
子线程执行
main线程状态---------->RUNNABLE
线程111111111状态-----------》RUNNABLE
子线1程在执行----------1111111111
线程111111111状态-----------》TERMINATED
main线程状态---------->RUNNABLE
线程222222222状态-----------》RUNNABLE
子线程2在执行----------2222222222
线程222222222状态-----------》TERMINATED
main线程状态---------->RUNNABLE
主线程还在执行
main线程状态---------->RUNNABLE
可以看到子线程在执行了join方法后线程状态由RUNNABLE变为TERMINATED
主线程一直都是RUNNABLE
所以,我们大概可以得到一个结论就是:
子线程thread1在调用join()方法后,thread1正常执行run,但是当前thread1所在的main线程就会被无限期阻塞,只有当thread1执行完,main线程才能恢复正常执行
因此,join() 方法使得调用该方法的那段代码所在的线程暂时阻塞
sleep()
sleep方法是和join()方法比较相似的方法
先看一个demo
@Slf4j
public class SleepDemo
static class ThreadA extends Thread
@Override
public void run()
synchronized (this)
log.info("------>"+this.getName());
log.info("------>"+Thread.currentThread().getName()+"begin:"+System.currentTimeMillis());
log.info("------>"+Thread.currentThread().getName()+"end:"+System.currentTimeMillis());
static class ThreadB extends Thread
private ThreadA threadA;
public ThreadB(ThreadA threadA)
this.threadA = threadA;
@Override
public void run()
synchronized (threadA)
log.info("------>"+Thread.currentThread().getName()+"begin:"+System.currentTimeMillis());
try
Thread.sleep(4000);
catch (InterruptedException e)
e.printStackTrace();
log.info("------>"+Thread.currentThread().getName()+"end:"+System.currentTimeMillis());
public static void main(String[] args)
ThreadA threadA = new ThreadA();
threadA.setName("AAAAAAAAAAAA");
ThreadB threadB = new ThreadB(threadA);
threadB.setName("BBBBBBBBBBBB");
threadB.start();
threadA.start();
结果:
22:17:19.259 [BBBBBBBBBBBB] INFO com.kevin.demo.base_of_cconcurrency.SleepDemo - ------>BBBBBBBBBBBBbegin:1562595439257
22:17:23.263 [BBBBBBBBBBBB] INFO com.kevin.demo.base_of_cconcurrency.SleepDemo - ------>BBBBBBBBBBBBend:1562595443263
22:17:23.263 [AAAAAAAAAAAA] INFO com.kevin.demo.base_of_cconcurrency.SleepDemo - ------>AAAAAAAAAAAA
22:17:23.263 [AAAAAAAAAAAA] INFO com.kevin.demo.base_of_cconcurrency.SleepDemo - ------>AAAAAAAAAAAAbegin:1562595443263
22:17:23.263 [AAAAAAAAAAAA] INFO com.kevin.demo.base_of_cconcurrency.SleepDemo - ------>AAAAAAAAAAAAend:1562595443263
可以看到,先执行的线程BBBBBBBBB的run方法,执行线程BBBBBBBB的run方法时,对线程AAAAAA对象加锁,只有一个线程能进入该方法执行,线程AAAAAAAAAA执行run方法时,因为线程AAAAAAAAAAA也对自己加锁,线程BBBBBBBBB还没有释放锁,所以线程AAAAAAAAAA不能执行其run方法中的同步块,需要等线程BBBBBBBBBB执行完成释放锁才能执行
所以,可以得到一个结论:
即 Thread.sleep(long) 方法是不释放对象锁的,并且使当前线程进入阻塞状态
join和sleep的区别
探究下和join()方法的区别,从上文中可以得到,join方法是当前线程执行join方法之后,会阻塞其所在的线程,等到当前执行join方法的线程执行完成,才会恢复正常执行状态
在上文中,我们可以得到,sleep()可以使线程休眠一段时间,使当前线程进入阻塞状态
通过一个demo,比较两个方法的差异
@Slf4j
public class SleepDemo
static class ThreadA extends Thread
@Override
public void run()
synchronized (this)
log.info("------>"+this.getName());
log.info("------>"+Thread.currentThread().getName()+"begin:"+System.currentTimeMillis());
log.info("------>"+Thread.currentThread().getName()+"end:"+System.currentTimeMillis());
static class ThreadB extends Thread
private ThreadA threadA;
public ThreadB(ThreadA threadA)
this.threadA = threadA;
@Override
public void run()
synchronized (threadA)
log.info("------>"+Thread.currentThread().getName()+"begin:"+System.currentTimeMillis());
try
log.info("-------->before: "+threadA.isAlive());
threadA.join();
log.info("---------->after: "+threadA.isAlive());
catch (InterruptedException e)
e.printStackTrace();
log.info("------>"+Thread.currentThread().getName()+"end:"+System.currentTimeMillis());
public static void main(String[] args) throws InterruptedException
ThreadA threadA = new ThreadA();
threadA.setName("AAAAAAAAAAAA");
ThreadB threadB = new ThreadB(threadA);
threadB.setName("BBBBBBBBBBBB");
threadB.start();
threadA.start();
和上文中的demo,只是做了些少改变
可以看到结果如下:
22:45:59.359 [BBBBBBBBBBBB] INFO com.kevin.demo.base_of_cconcurrency.SleepDemo - ------>BBBBBBBBBBBBbegin:1562597159357
22:45:59.363 [BBBBBBBBBBBB] INFO com.kevin.demo.base_of_cconcurrency.SleepDemo - -------->before: true
22:45:59.363 [AAAAAAAAAAAA] INFO com.kevin.demo.base_of_cconcurrency.SleepDemo - ------>AAAAAAAAAAAA
22:45:59.363 [AAAAAAAAAAAA] INFO com.kevin.demo.base_of_cconcurrency.SleepDemo - ------>AAAAAAAAAAAAbegin:1562597159363
22:45:59.363 [AAAAAAAAAAAA] INFO com.kevin.demo.base_of_cconcurrency.SleepDemo - ------>AAAAAAAAAAAAend:1562597159363
22:45:59.364 [BBBBBBBBBBBB] INFO com.kevin.demo.base_of_cconcurrency.SleepDemo - ---------->after: false
22:45:59.364 [BBBBBBBBBBBB] INFO com.kevin.demo.base_of_cconcurrency.SleepDemo - ------>BBBBBBBBBBBBend:1562597159364
可以得出,线程B会在先执行,但是之前如果使用sleep方法的话,线程B持有线程A对象的对象锁,只有当线程B执行完成并且释放锁,线程A才能执行其run方法,但是这个替换为join方法后,感觉上是线程B执行一会,然后线程A执行,到线程A执行完成并销毁,线程B继续执行,线程A之所以能执行,完全是线程B在执行join方法时,释放了锁,join() 方法是释放锁,而且和 wait() 方法的作用类似
总结
join和sleep区别
- sleep(long)方法在睡眠时不释放对象锁
- join(long)方法在等待的过程中释放对象锁
- sleep是让调用线程进入阻塞状态
sleep和wait区别
- sleep是Thread类的静态方法,wait是object的方法
- sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象锁。
而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备 - sleep方法需要抛异常,wait方法不需要
- sleep方法可以在任何地方使用,wait方法只能在同步方法和同步代码块中使用
以上是关于并发编程 -- join和sleep的主要内容,如果未能解决你的问题,请参考以下文章
Java 并发编程 -- 并发编程线程基础(线程创建与运行线程通知与等待join / sleep / yield方法线程中断线程上下文切换死锁守护线程与用户线程ThreadLocal)
Java 并发编程 -- 并发编程线程基础(线程创建与运行线程通知与等待join / sleep / yield方法线程中断线程上下文切换死锁守护线程与用户线程ThreadLocal)
Java并发之wait notify yield sleep join
线程间的协作wait,notify,sleep,yield,join