JAVA篇:Java 多线程 (一) 线程控制
1、线程控制
关键字:wait/notify/notifyAll、join、sleep、interrupt
线程控制讨论线程在调用了start()到执行完成中间阶段的行为,包含
-
线程阻塞和唤醒、超时等待
-
线程中断机制
1.1 线程阻塞和唤醒、超时等待
主要讨论join(),wait()、notify()和notifyAll(),以及yield()和sleep()。以及期间cpu资源及锁资源的情况,这里的锁仅仅考虑synchronized(Object)对象锁。
1.1.1 join() 方法
join()是线程的实例方法,有两种形式,thread.join()和tread.join(long timeout)。join方法会阻塞当前线程,等待指定线程运行完毕后才会被唤醒,或者如果设置了超时,等到超时后当前线程也会被唤醒。
join()方法使得当前线程休眠,释放cpu资源,但是并不会释放锁。
有说法说“其底层实现是wait()方法,会释放锁。”然后我写了一个测试代码,形成了死锁。wait()方法释放锁的相关讨论在后文,在这里先讨论join()方法运行过程中的情况。
join()的前缀是线程实例。如果要描述得清楚些则需要做一些假设。譬如说由线程A和t1,t1处于运行状态,在当前线程A调用t1.join()。那么线程A会无限阻塞,直到t1运行结束。
那么A在调用了t1.join()后等待t1的期间是否会释放资源呢?我感觉释放资源这个要往细了说,释放什么资源?释放cpu资源,释放cpu资源和全部锁资源,释放cpu资源和指定锁资源。
有说法join()方法调用之后会释放锁,总不可能是释放t1持有的锁吧,所以只能理解为释放当前线程A持有的锁。但是join方法不同,它是由t1调用的,也无法预见t1会需要什么锁资源,那么A调用t1.join()只可能是任意释放一个锁,或者说更加靠谱地释放全部锁。
我按照这个思路写了测试代码,但是主线程调用了join()并未释放任何锁,然后两个子线程都无法获得锁,造成了死锁。
为了防止死锁,我将join()方法设置了超时,使得代码可以运行,最后的代码和结果如下:
/* 测试join() */ public void test2(){ /* 模拟共享资源 */ Object res = new Object(); Object res2 = new Object(); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); /*创建子线程1,共享资源res*/ Runnable r1 = new Runnable() { @Override public void run() { try { Thread.sleep(3000); System.out.println(df.format(new Date())+" part11:子线程1休眠结束,尝试请求res锁"); synchronized (res){ System.out.println(df.format(new Date())+" part12:子线程1获得res锁,后进入休眠"); Thread.sleep(3000); System.out.println(df.format(new Date())+" part13:子线程1结束休眠,并释放res锁"); } } catch (InterruptedException e) { e.printStackTrace(); } } }; Thread t1 = new Thread(r1); t1.start(); /*创建子线程2,共享资源res2*/ Runnable r2 = new Runnable() { @Override public void run() { try { Thread.sleep(3000); System.out.println(df.format(new Date())+" part21:子线程2休眠结束,尝试请求res2锁"); synchronized (res2){ System.out.println(df.format(new Date())+" part22:子线程2获得res2锁,后进入休眠"); Thread.sleep(3000); System.out.println(df.format(new Date())+" part23:子线程2结束休眠,并释放res2锁"); } } catch (InterruptedException e) { e.printStackTrace(); } } }; Thread t2 = new Thread(r2); t2.start(); /* 主线程持有锁,然后调用join */ synchronized (res2){ System.out.println(df.format(new Date())+" part01:主线程持有res2锁"); synchronized (res){ System.out.println(df.format(new Date())+" part02:主线程持有res锁"); System.out.println(df.format(new Date())+" part03:主线程调用t1,t2的join()"); try { t1.join(10000);//有等待时限的join t2.join(10000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(df.format(new Date())+" part04:主线程退出join,不再等待,释放锁res"); } System.out.println(df.format(new Date())+" part05:主线程释放锁res2"); } System.out.println(df.format(new Date())+" 测试结束。"); }