本文共 6244 字,大约阅读时间需要 20 分钟。
最近看了这位博主的文章 写的挺好的 跟着里面的线程 温习了一遍 结尾处有道题算是复习巩固吧
我是用ReentrantLock实现的 而不是synchronized使用3个线程,要求三个线程顺序执行,不允许使用sleep()强制让线程有顺序。线程A输出1线程B输出2线程C输出3线程A输出4线程B输出5以此类推,一直输出到1000为止。
题目是不是看着不难 一开始我也是这么觉得的
后来我发现自己错了 还错了很离谱…… 也许是自己思维不够灵活 花了一下午时间解决…… 不过也算是初步掌握了 ReentrantLock的运用题目要求是ABC顺序依次输出结果
可线程是抢占式的,鬼会听你安排 一个一个输出哦 肯定都是抢着去占CPU
线程是并发式的 意思就是 看起来像是一起执行 实际是一次只能执行一次线程
那怎么才可以让线程乖乖听话?
答:一个执行的时候 另外两个等待不就OK了
比如 A执行的时候BC等待 A执行完后唤醒B执行 B执行完后唤醒C执行 C执行完后唤醒A执行 这样一直循环 就是ABCABC的执行顺序了
思路有了,那我们如何来实现呢?
一开始 脑子一蹦出来就是用synchronized
来解决 后来想想不行啊
synchronized的唤醒方式有2种(学的不深 不知道是否还有其他方法)
1. notify() 随机唤醒一个线程 2. notifyAll() 唤醒全部线程题目要求我们是按照顺序ABC来执行线程的 我滴个神啊 这tm不是断我路吗!
别急别急 还好有ReentrantLock
话说网上一直有争议 到底是ReentrantLock
处理并发好还是synchronized
好?
在并发量比较小的情况下,使用synchronized是个不错的选择,但是在并发量比较高的情况下,其性能下降很严重,此时ReentrantLock是个不错的方案
ReentrantLock
来解决顺序执行的问题?首先我们得创建ReentrantLock
锁和Condition
小黑屋
private ReentrantLock r = new ReentrantLock(); //创建锁 private Condition c1 = r.newCondition(); //小黑屋C1 private Condition c2 = r.newCondition(); //小黑屋C2 private Condition c3 = r.newCondition(); //小黑屋C3
上面小黑屋什么意思呢?
其实小黑屋是我给它取得名字 因为它很符合盒子的形象:
A执行的时候让BC等待 B,C就被关到c2,c3这两间小黑屋里 没有人来开门 它们就永远都无法出来(B,C等待唤醒)
多线程的常见问题 :A线程执行的时候B抢占CPU A停止手头工作等待 这会产生脏数据
等问题
private ReentrantLock r = new ReentrantLock(); //创建了锁 r.lock(); //添加锁 //代码A r.unlock(); // 释放锁
上面意思就是说 这块代码A被加锁了 无法被其他线程抢占
r.lock(); //添加锁 //代码A r.unlock(); // 释放锁 r.lock(); //添加锁 //代码B r.unlock(); // 释放锁
当线程A执行一个类中的 代码A时 线程B不能执行代码B 虽然他们不是同一个代码块
因为他们来自一个锁r(ReentrantLock)所谓人一心不可二用 这里也是这个道理。一个锁 只能锁一个代码块
所以 有两种情况不行:1. 线程A执行代码A 线程B执行代码A2. 线程A执行代码A 线程B执行代码B
.await()
函数派上用场了 这就是等待的意思 对应synchronized的wait()函数
线程执行 await() 等待后 当前位置(执行await())下面代码不会被执行
同时释放锁 如果被唤醒了 还是从之前被锁定的位置开始 也可以这么理解 :从哪里跌倒 ,从哪里爬起来在B线程里执行 c2.await();即可就是把B线程放进c2中等待唤醒
举个栗子:
线程B代码块{ r.lock(); //添加锁 c2.await(); //把B关进c2小黑屋 等待唤醒 r.unlock(); // 释放锁 }
这个时候我们需要一个变量 来指示 当前线程到底时A还是B
1代表A
2代表B
3代表C
public int flag = 1;线程A代码块{if (flag != 1) { c1.await(); //A关进小黑屋C1 } c2.signal(); //唤醒小黑屋c2里的家伙(这里指B)}
线程B代码块{if (flag != 2) { c2.await(); //B关进小黑屋C2 } c3.signal(); //唤醒小黑屋C3里的家伙(这里指C)}
线程C代码块{if (flag != 3) { c3.await(); //C关进小黑屋C3 } c1.signal(); //唤醒小黑屋c1里的家伙(这里指A)}
核心代码
举个栗子A:if(i==1001) { c2.signal(); //唤醒B c3.signal(); //唤醒C r.unlock(); //释放锁 }
r.unlock();
这个是关键 一定要写 不然到最后C线程无法停止
还有哦! 我是用 静态变量 i
来记录这个递增的数 因为是三个线程共享嘛
提一下哦 !因为ABC线程只执行一次 我是直接采用 匿名对象来实现(实际是有类名的 不过是借用了父类或者接口的类名) 这样子
package day12;import java.sql.Time;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;class Printer3 { private ReentrantLock r = new ReentrantLock(); private Condition c1 = r.newCondition(); private Condition c2 = r.newCondition(); private Condition c3 = r.newCondition(); public static int i = 1; public int flag = 1; public void print1() throws InterruptedException { r.lock(); if (flag != 1) { c1.await(); } if(i==1001) { c2.signal(); c3.signal(); r.unlock(); return; } System.out.println("线程A:" + i++); flag = 2; c2.signal(); r.unlock(); // 释放锁 } public void print2() throws InterruptedException { r.lock(); if (flag != 2) { c2.await(); } if(i==1001) { c1.signal(); c3.signal(); r.unlock(); return; } System.out.println("线程B:" + i++); flag = 3; c3.signal(); r.unlock(); } public void print3() throws InterruptedException { r.lock(); if (flag != 3) { c3.await(); } if(i==1001) { c1.signal(); c2.signal(); r.unlock(); return; } System.out.println("线程C:" + i++); flag = 1; c1.signal(); r.unlock(); }}public class thread { public static void main(String[] args) { final Printer3 p = new Printer3(); long startTime = System.currentTimeMillis(); new Thread() { public void run() { while (true) { try { if (p.i == 1001) { System.out.println("线程A结束"); break; } p.print1(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { public void run() { while (true) { try { if (p.i == 1001) { System.out.println("线程B结束"); break; } p.print2(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { public void run() { long startTime = System.currentTimeMillis(); //程序结束记录时间 while (true) { try { if (p.i == 1001) { System.out.println("线程C结束"); break; } p.print3(); } catch (InterruptedException e) { e.printStackTrace(); } } long endTime = System.currentTimeMillis(); //程序结束记录时间 long TotalTime = endTime - startTime; System.out.print("耗时:"+TotalTime); } }.start(); }}
实验结果:
最后提一下 线程 停止有多个方法 :
最好就是让他自己执行完run方法 自然消亡 而不是使用interrupt
强制打断 真的是千辛万苦……不过 也算是有点收获了
要是我的这篇博客可以解答你的些许问题,那也是极好的如果有问题 欢迎提出 一起学习 一起进步。