常用方法学习
- join
当在一个线程中调用另一个线程的join()方法时,当前线程转入阻塞状态,等待另一个线程执行结束后再继续执行当前线程。
示例:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
public class ThreadJoinDemo { public static void main(String[] args) throws InterruptedException { System.out.println("Main Thread start!"); Thread1 thread1 = new Thread1("A"); Thread thread2 = new Thread1("B"); thread1.start(); thread2.start(); //此处不添加join方法,则主线程会先结束,然后A、B线程结束 //添加join方法后,主线程会在A、B线程结束后再结束 thread1.join(); thread2.join(); System.out.println("Main Thread end!"); } static class Thread1 extends Thread { private String name; public Thread1(String name) { super(name); this.name = name; } @Override public void run() { System.out.println(Thread.currentThread().getName() + " start"); for (int i = 0; i < 5; i ++) { System.out.println("子线程" + name + "run:" + i); try { sleep((long) (Math.random() * 10)); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + " end"); } } }
如果把join方法注释掉,则结果如下,主线程先结束,然后线程A、B结束:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
Main Thread start! A start 子线程Arun:0 Main Thread end! B start 子线程Brun:0 子线程Brun:1 子线程Arun:1 子线程Arun:2 子线程Brun:2 子线程Brun:3 子线程Brun:4 子线程Arun:3 B end 子线程Arun:4 A end
join方法放开的话,则主线程会等A、B线程结束后再结束:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
Main Thread start! A start 子线程Arun:0 B start 子线程Brun:0 子线程Arun:1 子线程Brun:1 子线程Brun:2 子线程Arun:2 子线程Brun:3 子线程Arun:3 子线程Arun:4 A end 子线程Brun:4 B end Main Thread end!
- yield
线程回到可执行状态,允许具有相同优先级的其他线程执行。但是并非一定会执行其他线程,因为该线程可能被调度器再度调起。
与sleep()区别:sleep方法让当前线程回到停滞状态,在sleep持续时间内,该线程肯定不会执行。sleep时,会执行其他低优先级的线程;yield执行后,线程仍是可执行状态,低优先级线程仍然无法获得cpu资源。
代码示例:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
public class YieldDemo { static class YieldThread extends Thread { public YieldThread(String name) { super(name); } @Override public void run() { for (int i = 0; i < 50; i++) { System.out.println(this.getName() + "----" + i); if (i == 30) { //当i=30时,该线程把资源让给其他线程执行。 yield(); } } } } public static void main(String[] args) { YieldThread t1 = new YieldThread("a"); YieldThread t2 = new YieldThread("b"); t1.start(); t2.start(); } }
第一种情况,线程a未让出资源,继续执行:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
"C:\Program Files\Java\jdk1.7.0_80\bin\java" "-javaagent:D:\IntelliJ IDEA 2017.1\lib\idea_rt.jar=57766:D:\IntelliJ IDEA 2017.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.7.0_80\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\jce.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\jfxrt.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\resources.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\rt.jar;E:\workspace-sets\workspace-demo\thread-demo\target\classes" com.xc.thread.yield.YieldDemo a----0 a----1 a----2 a----3 a----4 a----5 a----6 a----7 a----8 a----9 a----10 a----11 a----12 a----13 a----14 a----15 a----16 a----17 a----18 a----19 a----20 a----21 a----22 a----23 a----24 a----25 a----26 a----27 a----28 a----29 a----30 a----31 a----32 a----33 a----34 a----35 a----36 a----37 a----38 a----39 a----40 a----41 a----42 a----43 a----44 a----45 a----46 a----47 a----48 a----49 b----0 b----1 b----2 b----3 b----4 b----5 b----6 b----7 b----8 b----9 b----10 b----11 b----12 b----13 b----14 b----15 b----16 b----17 b----18 b----19 b----20 b----21 b----22 b----23 b----24 b----25 b----26 b----27 b----28 b----29 b----30 b----31 b----32 b----33 b----34 b----35 b----36 b----37 b----38 b----39 b----40 b----41 b----42 b----43 b----44 b----45 b----46 b----47 b----48 b----49 Process finished with exit code 0
第二种情况,线程a和线程b执行到第30次时,都让出了资源:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
a----0 a----1 a----2 a----3 a----4 a----5 a----6 a----7 a----8 a----9 a----10 a----11 a----12 a----13 a----14 a----15 a----16 a----17 a----18 a----19 a----20 a----21 a----22 a----23 a----24 a----25 a----26 a----27 a----28 a----29 a----30 b----0 b----1 b----2 b----3 b----4 b----5 b----6 b----7 b----8 b----9 b----10 b----11 b----12 b----13 b----14 b----15 b----16 b----17 b----18 b----19 b----20 b----21 b----22 b----23 b----24 b----25 b----26 b----27 b----28 b----29 b----30 a----31 a----32 a----33 a----34 a----35 a----36 a----37 a----38 a----39 a----40 a----41 a----42 a----43 a----44 a----45 a----46 a----47 a----48 a----49 b----31 b----32 b----33 b----34 b----35 b----36 b----37 b----38 b----39 b----40 b----41 b----42 b----43 b----44 b----45 b----46 b----47 b----48 b----49 ============================ over ============================ Process finished with exit code 0
- wait和notify
wait:线程在synchronized代码块获得对象的锁后,通过wait()方法释放对象锁,同时线程休眠,直到其他线程调用对象的notify()方法唤醒该线程。
notify:唤醒一个正在等待该对象的线程。不过,notify并不是立即唤醒,而是在synchronized执行结束后唤醒。
代码示例:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
public class WaitNotifyDemo { static class PrintThread implements Runnable { private String name; private Object prev; private Object self; public PrintThread(String name, Object prev, Object self) { this.name = name; this.prev = prev; this.self = self; } @Override public void run() { int count = 10; while (count > 0) { //先锁住prev synchronized (prev) { //再锁self synchronized (self) { System.out.println(name); count--; //唤醒等待self的线程 self.notify(); } try { //当前线程阻塞,交出prev对象的控制 prev.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } public static void main(String[] args) throws InterruptedException { Object a = new Object(); Object b = new Object(); Object c = new Object(); PrintThread t1 = new PrintThread("A", c, a); //线程A持有c和a的对象锁 PrintThread t2 = new PrintThread("B", a, b); //线程B持有a和b的对象锁 PrintThread t3 = new PrintThread("C", b, c); //线程C持有b和c的对象锁 //线程A开始执行,先持有c,a的对象锁,打印完成后先释放a,唤醒了等待a锁的线程B;然后再释放了c,自身进入休眠状态。 new Thread(t1).start(); Thread.sleep(100);//确保线程按顺序执行 //线程B被唤醒后,这时a,b锁都没被占用,开始执行,并持有a、b的对象锁,打印操作完成了再释放了b,唤醒了等待b锁的c;然后再释放a,进入休眠状态 new Thread(t2).start(); Thread.sleep(100); //线程C被唤醒,申请了b,c的锁,打印完成后释放了c,又唤醒了等待c锁的线程A new Thread(t3).start(); Thread.sleep(100); } }
结果展示:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
"C:\Program Files\Java\jdk1.7.0_80\bin\java" "-javaagent:D:\IntelliJ IDEA 2017.1\lib\idea_rt.jar=58550:D:\IntelliJ IDEA 2017.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.7.0_80\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\jce.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\jfxrt.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\resources.jar;C:\Program Files\Java\jdk1.7.0_80\jre\lib\rt.jar;E:\workspace-sets\workspace-demo\thread-demo\target\classes" com.xc.thread.wait.WaitNotifyDemo
A
B
C
A
B
C
A
B
C
A
B
C
A
B
C
A
B
C
A
B
C
A
B
C
A
B
C
A
B
C
简单说明:
线程A最先执行,并持有了a和c的对象锁,在打印完成后,调用self.notify()实际是a.notify,这时唤醒了等待a锁的线程B。
在线程A里面的synchronized代码块执行完成后,a对象的锁被释放,线程B此时持有了a和b的对象锁,因此可以执行打印方法。打印完成后唤醒了等待b对象的线程c。这时线程A的第二个代码块也执行结束,释放了对象c,线程A进入休眠。
线程B唤醒线程C后释放了b对象,线程C可以执行,并持有了c和b的对象锁。再打印完释放对象c后,线程A又可以执行了。
- Lock和Condition
使用lock对象锁来控制多个线程对同一资源的同步互斥。使用Condition来控制多个线程之间的等待/通知。
Condition的await()方法用于释放资源并等待唤醒,类似object.wait();
Condition的signal()方法用于唤醒等待该资源的线程,类似object.signal();
下面一段代码是通过Lock和Condition来实现线程间的通信,是线程可以有序地交替执行:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
/** * 通过lock和condition实现线程间通信,使线程A打印3次后打印1次线程B * Created by xuec on 2019/3/7. */ public class LockDemo { private static Lock lock = new ReentrantLock(); private static Condition condition = lock.newCondition(); static class ThreadOne extends Thread { private String name; public ThreadOne(String name) { super(name); this.name = name; } @Override public void run() { for (int i = 1; i < 50; i++) { try { //线程A先执行,获得对象锁,这时线程B无法执行 lock.lock(); while (ThreadToGo.value == 2) { //当value为2时,线程A开始等待 condition.await(); } System.out.println(i + "---" + name); if (i % 3 == 0) { //线程A执行3次后,值改为2,下次执行时,会这是当前线程进入等待状态 ThreadToGo.value = 2; } //每次输出结束都会唤醒等待的线程 condition.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } } static class ThreadTwo extends Thread { private String name; public ThreadTwo(String name) { super(name); this.name = name; } @Override public void run() { for (int i = 1; i < 50; i++) { try { lock.lock(); while (ThreadToGo.value == 1) { condition.await(); } System.out.println(i + "---" + name); if (i % 3 != 0) { ThreadToGo.value = 1; } condition.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } } static class ThreadToGo { static int value = 1; } public static void main(String[] args) { ThreadOne t1 = new ThreadOne("A"); ThreadTwo t2 = new ThreadTwo("B"); t1.start(); t2.start(); } }
输出结果如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1---A 2---A 3---A 1---B 4---A 5---A 6---A 2---B 7---A 8---A 9---A 3---B 4---B 10---A 11---A 12---A 5---B 13---A 14---A 15---A 6---B 7---B 16---A 17---A 18---A 8---B 19---A 20---A 21---A 9---B 10---B 22---A 23---A 24---A 11---B 25---A 26---A 27---A 12---B 13---B 28---A 29---A 30---A 14---B 31---A 32---A 33---A 15---B 16---B 34---A 35---A 36---A 17---B 37---A 38---A 39---A 18---B 19---B 40---A 41---A 42---A 20---B 43---A 44---A 45---A 21---B 22---B 46---A 47---A 48---A 23---B 49---A