一、等待、通知之交叉备份
创建20个线程,10个线程备份到A数据库,
另一个10个线程备份到B数据库
要求,备份到数据库的线程交叉进行
package com.it.po.thread10; public class DBTools { volatile private boolean prevIsA=false; synchronized public void backupA(){ try { while (prevIsA==true){ wait(); } for(int i=0;i<5;i++){ System.out.println("*****"); } prevIsA=true; notifyAll(); } catch (InterruptedException e) { e.printStackTrace(); } } synchronized public void backupB(){ try { while (prevIsA==false){ wait(); } for(int i=0;i<5;i++){ System.out.println("@@@@@"); } prevIsA=false; notifyAll(); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread10; public class MyThread2_1 extends Thread { private DBTools dbTools; public MyThread2_1(DBTools dbTools) { this.dbTools = dbTools; } @Override public void run() { super.run(); dbTools.backupA(); } }
package com.it.po.thread10; public class MyThread2_2 extends Thread { private DBTools dbTools; public MyThread2_2(DBTools dbTools) { this.dbTools = dbTools; } @Override public void run() { super.run(); dbTools.backupB(); } }
package com.it.po.thread10; import com.sun.org.apache.bcel.internal.generic.NEW; public class Run2 { public static void main(String[] args) throws InterruptedException { DBTools dbTools = new DBTools(); MyThread2_1[] my1 = new MyThread2_1[20]; MyThread2_2[] my2 = new MyThread2_2[20]; for(int i=0;i<20;i++){ my1[i]=new MyThread2_1(dbTools); my2[i]=new MyThread2_2(dbTools); my1[i].start(); my2[i].start(); } } }
部分数据
***** ***** ***** ***** ***** @@@@@ @@@@@ @@@@@ @@@@@ @@@@@ ***** ***** ***** ***** ***** @@@@@ @@@@@ @@@@@ @@@@@ @@@@@
volatile private boolean prevIsA=false;
该行代码达到了交替备份的效果。
二、join的使用
很多情况主线程创建并启动子线程,如果子线程要进行大量的运算
,主线程往往将早于子线程结束之前结束。这是,如果主线程向等待子线程
执行完之后再结束,比如子线程处理一个数据,主线程要取得这个数据
中的值,就要用到join了。 join方法的作用就是等待线程对象销毁。
package com.it.po.thread10; public class MyThread3 extends Thread { @Override public void run() { super.run(); try { int time = (int) (Math.random() * 10000);//生成0-10s内的值 System.out.println("time= "+time); Thread.sleep(time);//time取得值是不确定的 System.out.println("MyThread3 线程执行结束了。。。 "); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread10; public class Run3 { public static void main(String[] args) throws InterruptedException { MyThread3 myThread3 = new MyThread3(); myThread3.start(); //Thread.sleep("??");这应该睡多久? //我希望执行完线程myThread3,再执行后面怎么办? myThread3.join(); System.out.println("MyThread3 线程执行结束了,到我了。。。"); } }
time= 5448
MyThread3 线程执行结束了。。。
MyThread3 线程执行结束了,到我了。。。
join方法的作用就是使线程 myThread3 无限期阻塞,直到的等待线程 myThread3
销毁后再执行后面的代码,join具有让线程排队运行的作用,有些类似同步的效果
join 和 sychronized区别
join在内部使用wait()方法进行等待,
sychronized 是使用“ 对象监视器” 原理作为同步。
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) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } }
三、join与异常
如果当前线程对象呗中断,则当前线程出现异常
package com.it.po.thread10; public class MyThred4_1 extends Thread { @Override public void run() { super.run(); for(int i=0;i<Integer.MAX_VALUE;i++){ String string = new String(); string.toString(); } } }
package com.it.po.thread10; public class MyThred4_2 extends Thread { @Override public void run() { super.run(); try { MyThred4_1 my1 = new MyThred4_1(); my1.start(); my1.join(); System.out.println("我是MyThred4_2线程 执行结束了。。。"); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread10; public class MyThred4_3 extends Thread { private MyThred4_2 myThred4_2; public MyThred4_3(MyThred4_2 myThred4_2) { this.myThred4_2 = myThred4_2; } @Override public void run() { super.run(); myThred4_2.interrupt(); } }
package com.it.po.thread10; import com.it.po.thread8.MyThread4_2; public class Run4 { public static void main(String[] args) throws InterruptedException { MyThred4_2 myThred4_2 = new MyThred4_2(); myThred4_2.start(); Thread.sleep(500); MyThred4_3 myThread4_3 = new MyThred4_3(myThred4_2); myThread4_3.start(); } }
我是MyThred4_2线程 执行结束了。。。 java.lang.InterruptedException at java.lang.Object.wait(Native Method) at java.lang.Thread.join(Thread.java:1252) at java.lang.Thread.join(Thread.java:1326) at com.it.po.thread10.MyThred4_2.run(MyThred4_2.java:9)
说明:join遇到interrupt会报异常。 但是按钮还是红色

原因是:线程Myread4_1还在执行。所以异常对于Myread4_1是不影响的。
四、join(long) 的使用
该方法的设定是等待的时间。
package com.it.po.thread10; public class MyThread5 extends Thread { @Override public void run() { super.run(); try { System.out.println("begin time "+System.currentTimeMillis()); Thread.sleep(8000); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread10; public class Run5 { public static void main(String[] args) throws InterruptedException { MyThread5 myThread5 = new MyThread5(); myThread5.start(); myThread5.join(2000); System.out.println("end time = "+System.currentTimeMillis()); } }
begin time 1575082943604 end time = 1575082945605
注意:join(2000)是两秒之后会执行main方法
实际两秒之后线程myThread5还是在运行的,知道8s之后才结束

join(long)和sleep(long)的区别
join(long)功能在内部是使用wait(llong)方法实现的,所以join(long)方法具有释放锁的特点
反之,sleep(long) 是不释放锁的只是把cpu资源让给其他线程。
五、join()后面的代码提前运行
package com.it.po.thread10; public class ThreadA extends Thread { private ThreadB b; public ThreadA(ThreadB b) { this.b = b; } @Override public void run() { super.run(); try { synchronized (b){ String name = Thread.currentThread().getName(); System.out.println("线程 "+name +" begin time= "+System.currentTimeMillis()); Thread.sleep(5000); System.out.println("线程 "+name +" end time= "+System.currentTimeMillis()); } } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread10; public class ThreadB extends Thread { @Override synchronized public void run() { super.run(); try { String name = Thread.currentThread().getName(); System.out.println("线程 "+name +" begin time= "+System.currentTimeMillis()); Thread.sleep(5000); System.out.println("线程 "+name +" end time= "+System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread10; public class Run6 { public static void main(String[] args) throws InterruptedException { ThreadB b = new ThreadB(); ThreadA a = new ThreadA(b); a.start(); b.start(); a.setName("A"); b.setName("B"); b.join(2000); System.out.println("main time = "+System.currentTimeMillis()); } }
结果1:
线程 A begin time= 1575086094626 线程 A end time= 1575086099626 main time = 1575086099626 线程 B begin time= 1575086099626 线程 B end time= 1575086104630
结果2
线程 B begin time= 1575086488113 线程 B end time= 1575086493114 main time = 1575086493114 线程 A begin time= 1575086493114 线程 A end time= 1575086498115
结果3
线程 B begin time= 1575086523049 线程 B end time= 1575086528049 线程 A begin time = 1575086528049 main time = 1575086528049 线程 A end time= 1575086533050
结果1分析:
1) b.join(2000) 先抢到B锁,然后将B锁进行释放
2)线程A抢到锁,打印,并且sleep(2000),
3)线程A打印结束,并释放锁
4)这时join(2000)和线程B争抢锁,且join(2000)再次抢到锁,发现时间已过,释放锁
打印main
6)5s过后线程B打印结束。
结果3分析
和上面差不多,其实还是
join(2000)和线程B争抢锁,且join(2000)再次抢到锁,发现时间已过,释放锁
注意看打印时间,线程A刚打印begin的时候,这时main time异步输出
浙公网安备 33010602011771号