JUC基础-3-线程间通信
AI-摘要
切换
FanXYのAI摘要 GPT
AI初始化中...
介绍自己
生成本文简介
推荐相关文章
前往主页
前往tianli博客
本文最后更新于 2023-09-19,文章内容可能已经过时。
3 线程间通信
线程间通信的模型有两种:共享内存和消息传递,以下方式都是基本这两种模型来实现的。我们来基本一道面试常见的题目来分析
场景—两个线程,一个线程对当前数值加 1,另一个线程对当前数值减 1,要求用线程间通信
同时这里要补充一点,我们在进入线程通信的等待唤醒的条件判断,应该使用 while
,这样能保证不会出现虚假唤醒,因为java的唤醒机制是,从哪里睡眠,就从哪里唤醒,如果使用if
导致出现并不应该唤醒的情况下,线程被唤醒,错误的执行了不该执行的方法。
3.1 synchronized 方案
class share {
// 资源
private int numbers = 0;
// +1 的方法
public synchronized void incr() throws InterruptedException {
// 第二步 判断 干活 通知
while (numbers == 1) {
this.wait();
}
// 如果 number 值为 0 就 + 1
numbers++;
this.notifyAll();
System.out.println(Thread.currentThread().getName() + " :: " + numbers);
}
// -1 的方法
public synchronized void decr() throws InterruptedException {
while (numbers != 1) {
this.wait();
}
numbers--;
this.notifyAll();
System.out.println(Thread.currentThread().getName() + " :: " + numbers);
}
}
public class ThreadDemo1 {
public static void main(String[] args) {
share share = new share();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
share.incr();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, "AA").start();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
share.decr();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, "BB").start();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
share.incr();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, "CC").start();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
share.decr();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, "DD").start();
}
}
3.2 Lock 方案
切记使用 Lock 接口,一定要使用 try finally 环绕,防止出现异常之后导致死锁。
class Share {
private int numbers = 0;
private final Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
// + 1 方法
public void incr() throws InterruptedException {
try {
lock.lock();
while (numbers == 1){
condition.await();
}
numbers ++;
System.out.println(Thread.currentThread().getName() + " :: " + numbers);
condition.signalAll();
} finally {
lock.unlock();
}
}
// - 1 方法
public void decr() throws InterruptedException {
try {
lock.lock();
while (numbers != 1){
condition.await();
}
numbers --;
System.out.println(Thread.currentThread().getName() + " :: " + numbers);
condition.signalAll();
} finally {
lock.unlock();
}
}
}
public class ThreadDemo2 {
public static void main(String[] args) {
Share share = new Share();
new Thread(()-> {
try {
for (int i = 0; i < 10; i++) {
share.incr();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, "AA").start();
new Thread(()-> {
try {
for (int i = 0; i < 10; i++) {
share.decr();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, "BB").start();
new Thread(()-> {
try {
for (int i = 0; i < 10; i++) {
share.incr();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, "CC").start();
new Thread(()-> {
try {
for (int i = 0; i < 10; i++) {
share.decr();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, "DD").start();
}
}
3.3 线程间定制化通信
3.3.1 案例介绍
问题: A 线程打印 5 次 A,B 线程打印 10 次 B,C 线程打印 15 次 C,按照此顺序循环 10 轮
这里定义 flag 是为了线程阻塞的时候,能按情况进行阻塞,如果flag不到达合法值就一直等待被唤醒,而为什么设置三个condition
,是因为唤醒的时候就不用使用 signalAll
了,要不然还要全部唤醒,进入二次竞争,然后再次等待,每进行一次唤醒,就进行一次竞争行为,效率降低。
class ThreadShare {
private Lock lock = new ReentrantLock();
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();
private int flag = 0;
public void print5(int loop) throws InterruptedException {
try {
lock.lock();
while (flag != 0) {
c1.await();
}
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " :: " + i + " 轮数 : " + loop + " flag : " + flag);
}
flag = (flag + 1) % 3;
c2.signal();
} finally {
lock.unlock();
}
}
public void print10(int loop) throws InterruptedException {
try {
lock.lock();
while (flag != 1) {
c2.await();
}
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " :: " + i + " 轮数 : " + loop);
}
flag = (flag + 1) % 3;
c3.signal();
} finally {
lock.unlock();
}
}
public void print15(int loop) throws InterruptedException {
try {
lock.lock();
while (flag != 2) {
c3.await();
}
for (int i = 0; i < 15; i++) {
System.out.println(Thread.currentThread().getName() + " :: " + i);
}
flag = (flag + 1) % 3;
c1.signal();
} finally {
lock.unlock();
}
}
}
public class ThreadDemo3 {
public static void main(String[] args) {
ThreadShare share = new ThreadShare();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
share.print5(i);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "AA").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
share.print10(i);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "BB").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
share.print15(i);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "CC").start();
}
}
synchronized实现同步的基础:Java中的每一个对象都可以作为锁。具体表现为以下3种形式。
对于普通同步方法,锁是当前实例对象。
对于静态同步方法,锁是当前类的class对象。
对于同步方法块,锁是synchonized括号里配置的对象
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用CC BY-NC-ND 4.0协议,完整转载请注明来自FanXYの秘密基地
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果