赞
踩
1.synchronized中的对象锁是线程的实例
我们可以使用同步语句块的方式对需要同步的代码进行包裹。
- Object obj = new Object();
- synchronized(obj){
- obj.wait(); //线程在这里等待
- }
此时线程会在obj.wait()处等待,如果想继续执行,此时需要别的线程通过notify、notifyAll唤醒或者中断。但是如果obj是一个线程实例会怎么样呢?
如下面的例子:
- public class HighConcurrency {
-
- public static void main(String[] args) {
- try {
- Thread threadTest = new Thread(){
- public void run(){
- System.out.println("执行线程中方法");
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- };
- threadTest.start();
- synchronized(threadTest){
- threadTest.wait(); //当线程终止的时候,会调用线程自身的notifyAll()方法
- }
- System.out.println("执行到了这里");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
输出:
- 执行线程中方法
- 执行到了这里
首先开始线程threadTest,在主线程执行到threadTest.wait()时,主线程会等待,奇怪的是主线程并没有别的线程使用notify或notifyAll方法唤醒,竟然直接执行了后面的语句"执行了这里"。查阅发现如果synchronized获得对象锁是线程的实例时,此时比较特殊,当该线程终止的时候,会调用线程自身的notifyAll()方法,会通知所有等待在该线程对象上的线程。
这有什么应用呢?
如下,我开启一个子线程计算一段数据,我希望计算完后最终结果后在主线程中再输出一段话(就当是对计算结果的反馈),此时可以采用类似上面的这段代码。
- class MyThread extends Thread {
- @Override
- public void run() {
- try {
- int secondValue = (int) (Math.random() * 3000);
- System.out.println(secondValue);
- Thread.sleep(secondValue);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- public class HighConcurrency {
-
- public static void main(String[] args) {
- try {
- MyThread threadTest = new MyThread();
- threadTest.start();
- synchronized(threadTest){
- threadTest.wait(); //当线程终止的时候,会调用线程自身的notifyAll()方法
- }
- System.out.println("我想当threadTest对象执行完毕后我再执行,我做到了");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
输出:
- 1135
- 我想当threadTest对象执行完毕后我再执行,我做到了
过程不多解释,就是利用了线程实例做对象锁时,在线程执行完后,会调用线程自身的notifyAll()方法,此时主线程会接着执行,用处可以控制线程的执行顺序,例如我可以让子线程做计算,在子线程计算完后,在主线程中输出计算结果。
2.Join的原理
Java中的join方法也可以控制线程的执行顺序,上面的代码的功能使用join方法也可以很方便的实现:
- class MyThread extends Thread {
- @Override
- public void run() {
- try {
- int secondValue = (int) (Math.random() * 3000);
- System.out.println(secondValue);
- Thread.sleep(secondValue);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- public class HighConcurrency {
-
- public static void main(String[] args) {
- try {
- MyThread threadTest = new MyThread();
- threadTest.start();
- threadTest.join();
- System.out.println("我想当threadTest对象执行完毕后我再执行,我做到了");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
输出的时功能一样。查看join的核心源码如下:
- public final void join() throws InterruptedException {
- join(0);
- }
- public final synchronized void join(long millis)
- throws InterruptedException {
- long base = System.currentTimeMillis();
- long now = 0;
-
- if (millis < 0) {
- throw new IllegalArgumentException("timeout value is negative");
- }
- if (millis == 0) { //如果时执行的join(0)
- while (isAlive()) { //如果线程是运行状态,就会执行下面的等待
- wait(0);
- }
- } else { //如果是执行的join(time)
- while (isAlive()) { //如果线程时运行状态
- long delay = millis - now;
- if (delay <= 0) {
- break;
- }
- wait(delay); //等待delay时间后自动返回继续执行
- now = System.currentTimeMillis() - base;
- }
- }
- }
其中核心代码如下:
- while (isAlive()) { //调用join方法线程是运行时状态
- wait(0); //进入等待
- }
isAlive()方法下面会做详细的介绍,先看wait(0),wait(0)是什么意思呢,查看下面wait()方法源码,其实wait()方法就是调用了wait(0)方法实现的,wait(0)就是让其一直等待。到这里会发现,其实join方法本质就是利用上面的线程实例作为对象锁的原理,当线程终止时,会调用线程自身的notifyAll()方法,通知所有等待在该线程对象上的线程的特征。
- public final void wait() throws InterruptedException {
- wait(0);
- }
3.下面再说明一下while(isAlive)语句的作用:
有下面的一段简单代码:
代码实现:
- public class HighConcurrency {
-
- // 1.现在有T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行
- final Thread t2 = new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- // 引用t1线程,等待t1线程执行完
- t1.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("t2");
- }
- });
- Thread t3 = new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- // 引用t2线程,等待t2线程执行完
- t2.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("t3");
- }
- });
- t3.start();//这里三个线程的启动顺序可以任意,大家可以试下!
- t2.start();
- }
- }
程序输出:
- t2
- t3
首先执行了t3.start(),在t3线程里的run方法里执行了t2.join(),此时有两种情况,可能还没有执行t2.start(),t2处于初始状态,也有可能执行了t2.start(),t2处于运行时状态,所以看到这里就明白了,join源码中while(isAlive()),其实相当于while(this.isAlive())就相当于判断这里的t2是不是已经是不是运行状态(有没有调用start方法)。这么设计是为了防止t2线程没有运行,此时t3线程如果直接执行wait(0)方法,那么将会一直等待下去,造成代码卡死。
为了验证,代码改为:
- t3.start();
- try {
- Thread.sleep(10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- t2.start();
此时的输出:
- t3
- t2
分析:将t2.start()和t3.start()之间的时间间隔变长,这样在t3线程中不会执行t2.join()时,保证了t2时处于初始状态还不是运行状态,此时while(isAlive())不成立,不会执行wait(0)方法,所以t3线程不会等待,会先输出t3。
然后再更改代码如下,保证在执行t2.join()时,t2.start()已经执行,t2已经处于运行状态:
- public class HighConcurrency {
-
- public static void main(String[] args) {
-
- final Thread t2 = new Thread(new Runnable() {
-
- @Override
- public void run() {
- System.out.println("t2");
- }
- });
- Thread t3 = new Thread(new Runnable() {
-
- @Override
- public void run() {
- try {
- Thread.sleep(10); //保证此时t2已经是运行时状态了
- t2.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("t3");
- }
- });
- t3.start();
- t2.start();
- }
- }
此时的输出:
- t2
- t3
分析:在t3线程中执行t2.join()方法前先执行了sleep(10)方法,保证在执行t2.join()时,t2已经是运行时状态了,所以此时t3会执行wait(0)方法等待,直到t2先执行完,t3再继续执行,所以输出t2 t3。
参考:
http://uule.iteye.com/blog/1101994
https://www.nowcoder.com/questionTerminal/d8a288fc416c4d638dfb042e1be239fc?from=14pdf
http://www.cnblogs.com/paddix/p/5381958.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。