A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 wygsqsj 于 2015-11-20 00:12 编辑

        当一个线程使用了join()方法,其他线程会抛弃cpu的执行权,当此线程结束是,其他线程才会运行,join()让线程获取到执行权直至结束。比如线程B调用到了线程A的join()方法,那么线程B就挂起,直到线程A执行完,线程B才能运行。这个地方有一个小疑惑,不知道大家有没有,就是我们在同步中,线程B执行到线程A的join方法后,线程B有没有抛锁,带着这个疑惑我做了个小实验:
  1. package edu.heima;

  2. //定义A类线程
  3. class Mythread implements Runnable
  4. {
  5.         public void run()
  6.         {
  7.                         for(int x = 1;x<=30;x++)
  8.                         {
  9.                                 System.out.println(Thread.currentThread().getName()+"..."+x);
  10.                                
  11.                                 //休息是为了让其他线程能抢到cpu执行权
  12.                                 try {
  13.                                         Thread.sleep(10);
  14.                                 } catch (InterruptedException e) {
  15.                                        
  16.                                         e.printStackTrace();
  17.                                 }
  18.                                
  19.                         }
  20.                         System.out.println(Thread.currentThread().getName()+"end");
  21.         }
  22. }

  23. //定义B类线程
  24. class Mythread1 implements Runnable
  25. {
  26.         //接收一个线程进来以便使用它的join方法
  27.         private Thread t;
  28.         Mythread1(Thread t)
  29.         {
  30.                 this.t = t;
  31.         }
  32.         public void run()
  33.         {
  34.                 //把join()方法添加到同步代码块中
  35.                 synchronized (Mythread.class) {
  36.                         for(int x = 1;x<=50;x++)
  37.                         {
  38.                                 System.out.println(Thread.currentThread().getName()+"..."+x);
  39.                                 //当打印到20时,我们调用A类线程的join方法,让正在运行的线程挂起
  40.                                 if(x==20)
  41.                                 {
  42.                                         System.out.println(Thread.currentThread().getName()+"打印到20啦");       
  43.                                         //我们调用睡眠方法是看看如果锁被抛弃了,那么肯定有其他线程参与进来,
  44.                                         //这是为了让其他线程能抢到执行权
  45.                                         try {
  46.                                                 Thread.sleep(50);
  47.                                                 t.join();
  48.                                         } catch (InterruptedException e) {
  49.                                                 e.printStackTrace();
  50.                                         }
  51.                                 }
  52.                                
  53.                         }
  54.                         //当打印完50时,代表此线程运行结束,打印“end”
  55.                         System.out.println(Thread.currentThread().getName()+"end");       
  56.                 }
  57.         }
  58. }

  59. class ThradeDemo {

  60.         public static void main(String[] args) {
  61.                
  62.                 //定义A类线程,起名小宏
  63.                 Mythread s = new  Mythread();
  64.                 Thread t1 = new Thread(s);
  65.                 t1.setName("小宏");
  66.                
  67.                 //定义B类线程,起名小绿、小花、小黑,并把把小宏传到B类线程中
  68.                 Mythread1 m = new Mythread1(t1);
  69.                 Thread t2 = new Thread(m);
  70.                 t2.setName("小绿");
  71.                 Thread t3 = new Thread(m);
  72.                 t3.setName("小花");
  73.                 Thread t4 = new Thread(m);
  74.                 t4.setName("小黑");
  75.                
  76.        
  77.                 for(int x = 0;x<=20;x++)
  78.                 {
  79.                         try {
  80.                                 Thread.sleep(50);
  81.                         } catch (InterruptedException e) {
  82.                                 // TODO Auto-generated catch block
  83.                                 e.printStackTrace();
  84.                         }
  85.                         System.out.println("main..."+x);
  86.                         //当主线程打印到7时,开启全部线程
  87.                         if(x==7)
  88.                         {
  89.                                 t1.start();
  90.                                 t2.start();
  91.                                 t3.start();
  92.                                 t4.start();
  93.                         }
  94.                        
  95.                        
  96.                 }       
  97.         }
  98. }
复制代码


         打印结果为:首先主线程打印到7,然后开启所有线程,这是主线程开始跟A类线程小宏和B类线程(因为加了同步,B类线程中只有一个在打印)抢夺cpu,打印结果随机,当B类线程打印到20时,执行到了小宏的join方法,这时B类线程挂起,只剩下小宏和主线程在抢夺cpu执行权,这时候我运行了很多次,发现当B类线程被挂起时,同步外的B类线程也没有打印20以前的数值,我推断虽然join方法让线程挂起了,但是并没有抛弃同步中的锁,所以每次打印结果都是B类线程等待小宏打印完”end“后继续打印剩下的内容,执行完同步中内容然后线程结束。当小宏”end“后就剩下了B类线程和主线程抢夺cpu,这时候打印结果就不用多说了。

8 个回复

倒序浏览
多线程还没学到。。先前来看看
回复 使用道具 举报
有意思的问题  不过我运行时注意到另一个问题:
  就是一开始进入的b线程好像一定是小绿,这个事不会变的吗?
然后小宏完事之后 就轮到b线程了  也就是说b线程中的小花或者小黑去抢这把锁  谁抢到谁运行。
然后我就发现一只都是小黑抢到,一开始以为是固定的顺序,后来又增加了几个角色,发现总有那么几个位置是固定的,比如 一直是小黑先执行 然后是小花,特们两个都是最后两个才执行

所以我的疑问是他们是某些顺序固定的吗,还是完全随机,只是我电脑问题重复次数比较多而已
回复 使用道具 举报
刚开始的时候是小绿和小宏一起抢资源
回复 使用道具 举报
xiao15779706 发表于 2015-11-20 09:21
有意思的问题  不过我运行时注意到另一个问题:
  就是一开始进入的b线程好像一定是小绿,这个事不会变的吗 ...

首先是主线程打印到7,然后小宏开启、小绿开启、小花开启、小黑开启,根据推断,不一定是小绿先执行,因为当小绿开启后他不一定进入运行状态,虽然我们的程序运行n多次都是小绿先进去的,如果我们在B类线程的run方法中加个等待就可以看出来。
  1. //定义B类线程
  2. class Mythread1 implements Runnable
  3. {
  4.         boolean flag = false;

  5.         //接收一个线程进来以便使用它的join方法
  6.         private Thread t;
  7.         Mythread1(Thread t)
  8.         {
  9.                 this.t = t;
  10.         }
  11.         public void run()
  12.         {
  13.                 if(flag)
  14.                         try {
  15.                                 Thread.sleep(100);
  16.                         } catch (InterruptedException e1) {
  17.                                 // TODO Auto-generated catch block
  18.                                 e1.printStackTrace();
  19.                         }
  20.                 //把join()方法添加到同步代码块中
  21.                 synchronized (Mythread.class) {
  22.                         for(int x = 1;x<=50;x++)
  23.                         {
  24.                                 System.out.println(Thread.currentThread().getName()+"..."+x);
  25.                                 //当打印到20时,我们调用A类线程的join方法,让正在运行的线程挂起
  26.                                 if(x==20)
  27.                                 {
  28.                                         System.out.println(Thread.currentThread().getName()+"打印到20啦");       
  29.                                         //我们调用睡眠方法是看看如果锁被抛弃了,那么肯定有其他线程参与进来,
  30.                                         //这是为了让其他线程能抢到执行权
  31.                                         try {
  32.                                                 Thread.sleep(50);
  33.                                                 t.join();
  34.                                         } catch (InterruptedException e) {
  35.                                                 e.printStackTrace();
  36.                                         }
  37.                                 }
  38.                                
  39.                         }
  40.                         //当打印完50时,代表此线程运行结束,打印“end”
  41.                         System.out.println(Thread.currentThread().getName()+"end");       
  42.                 }
  43.         }
  44. }
复制代码

然后在主线程中修改:
  1. t1.start();
  2.                                 t2.start();
  3.                                 m.flag = true;
  4.                                 t3.start();
  5.                                 t4.start();
复制代码
回复 使用道具 举报
还没学到 ,先过来学习下
回复 使用道具 举报
wygsqsj 发表于 2015-11-20 19:32
首先是主线程打印到7,然后小宏开启、小绿开启、小花开启、小黑开启,根据推断,不一定是小绿先执行,因 ...

那也就说他们的顺序是完全随机的咯
回复 使用道具 举报
wygsqsj 中级黑马 2015-11-20 22:56:50
8#
xiao15779706 发表于 2015-11-20 22:48
那也就说他们的顺序是完全随机的咯

可以这么说,但要考虑同步,同步外的即使是得到cpu执行权也进不去,同步内的被挂起也不会抛锁。
回复 使用道具 举报
wygsqsj 中级黑马 2015-11-20 22:58:40
9#
xiao15779706 发表于 2015-11-20 22:48
那也就说他们的顺序是完全随机的咯

可以这么说,但要考虑同步,同步外的即使是得到cpu执行权也进不去,同步内的被挂起也不会抛锁。
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马