Java多线程通信:交替打印ABAB实例
作者:buptmumu 发布时间:2022-04-08 06:57:28
使用wait()和notify()实现Java多线程通信:两个线程交替打印A和B,如ABABAB
public class Test {
public static void main(String[] args) {
final PrintAB print = new PrintAB();
new Thread(new Runnable() {
public void run(){
for(int i=0;i<5;i++) {
print.printA();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
for(int i=0;i<5;i++) {
print.printB(); }
}
}).start();
}
}
class PrintAB{
private boolean flag = true;
public synchronized void printA () {
while(!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
} }
System.out.print("A");
flag = false;
this.notify();
}
public synchronized void printB () {
while(flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("B");
flag = true;
this.notify(); }
}
补充知识:Java多个线程顺序打印数字
要求
启动N个线程, 这N个线程要不间断按顺序打印数字1-N. 将问题简化为3个线程无限循环打印1到3
方法一: 使用synchronized
三个线程无序竞争同步锁, 如果遇上的是自己的数字, 就打印. 这种方式会浪费大量的循环
public class TestSequential1 {
private volatile int pos = 1;
private volatile int count = 0;
public void one(int i) {
synchronized (this) {
if (pos == i) {
System.out.println("T-" + i + " " + count);
pos = i % 3 + 1;
count = 0;
} else {
count++;
}
}
}
public static void main(String[] args) {
TestSequential1 demo = new TestSequential1();
for (int i = 1; i <=3; i++) {
int j = i;
new Thread(()->{
while(true) {
demo.one(j);
}
}).start();
}
}
}
输出
T-1 0
T-2 5793
T-3 5285
T-1 2616
T-2 33
T-3 28
T-1 22
T-2 44
T-3 6
T-1 881
T-2 118358
T-3 247380
T-1 30803
T-2 29627
T-3 52044
...
方法二: 使用synchronized配合wait()和notifyAll()
竞争同步锁时使用wait()和notifyAll(), 可以避免浪费循环
public class TestSequential4 {
private volatile int pos = 1;
private volatile int count = 0;
private final Object obj = new Object();
public void one(int i) {
System.out.println(i + " try");
synchronized (obj) {
System.out.println(i + " in");
try {
while (pos != i) {
count++;
System.out.println(i + " wait");
obj.wait();
}
System.out.println("T-" + i + " " + count);
pos = i % 3 + 1;
count = 0;
obj.notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
TestSequential4 demo = new TestSequential4();
for (int i = 3; i >=1; i--) {
int j = i;
new Thread(()->{
while(true) {
demo.one(j);
}
}).start();
}
}
}
输出
3 try
3 in
3 wait
2 try
2 in
2 wait
1 try
1 in
T-1 2
1 try
1 in
1 wait
T-2 1
2 try
2 in
2 wait
T-3 1
3 try
3 in
3 wait
2 wait
T-1 2
1 try
1 in
1 wait
T-2 1
2 try
2 in
2 wait
T-3 1
3 try
3 in
3 wait
2 wait
T-1 2
1 try
1 in
1 wait
T-2 1
2 try
2 in
2 wait
T-3 1
3 try
3 in
3 wait
2 wait
T-1 2
1 try
1 in
1 wait
T-2 1
2 try
2 in
2 wait
T-3 1
3 try
3 in
3 wait
2 wait
T-1 2
1 try
1 in
1 wait
T-2 1
2 try
2 in
2 wait
T-3 1
3 try
3 in
3 wait
2 wait
T-1 2
1 try
1 in
1 wait
T-2 1
2 try
2 in
2 wait
T-3 1
3 try
3 in
3 wait
2 wait
T-1 2
1 try
1 in
1 wait
T-2 1
2 try
2 in
2 wait
T-3 1
3 try
3 in
3 wait
2 wait
T-1 2
1 try
1 in
1 wait
T-2 1
2 try
2 in
2 wait
T-3 1
3 try
3 in
3 wait
2 wait
T-1 2
1 try
1 in
1 wait
T-2 1
2 try
2 in
2 wait
T-3 1
3 try
3 in
3 wait
2 wait
T-1 2
...
方法三: 使用可重入锁
用Lock做, 非公平锁, 三个线程竞争, 如果遇上的是自己的数字, 就打印. 这种方式也会浪费大量的循环
public class TestSequential2 {
private final Lock lock = new ReentrantLock();
private volatile int pos = 1;
private volatile int count = 0;
public void one(int i) {
lock.lock();
if (pos == i) {
System.out.println("T-" + i + " " + count);
pos = i % 3 + 1;
count = 0;
} else {
count++;
}
lock.unlock();
}
public static void main(String[] args) {
TestSequential2 demo = new TestSequential2();
for (int i = 1; i <=3; i++) {
int j = i;
new Thread(()->{
while(true) {
demo.one(j);
}
}).start();
}
}
}
输出
T-1 0
T-2 0
T-3 323
T-1 54
T-2 68964
T-3 97642
T-1 6504
T-2 100603
T-3 6989
T-1 1313
T-2 0
T-3 183741
T-1 233
T-2 5081
T-3 164367
..
方法四: 使用可重入锁, 启用公平锁
和3一样, 但是使用公平锁, 这种情况下基本上可以做到顺序执行, 偶尔会产生多一次循环
private final Lock lock = new ReentrantLock(true);
输出
T-1 0
T-2 0
T-3 0
T-1 0
T-2 0
T-3 0
T-1 0
T-2 0
T-3 0
T-1 0
T-2 0
T-3 1
T-1 1
T-2 1
T-3 1
...
方法五: 使用Condition
每个线程如果看到不是自己的计数, 就await(), 如果是自己的计数, 就完成打印动作, 再signalAll()所有其他线程去继续运行, 自己在下一个循环后, 即使又继续执行, 也会因为计数已经变了而await.
如果ReentrantLock构造参数使用true, 可以基本消除 ~await 这一步的输出.
public class ReentrantLockCondition2 {
private static Lock lock = new ReentrantLock();
private static Condition condition = lock.newCondition();
private volatile int state = 1;
private void handle(int state) {
lock.lock();
try {
while(true) {
while(this.state != state) {
System.out.println(state + " ~await");
condition.await();
}
System.out.println(state);
this.state = state % 3 + 1;
condition.signalAll();
System.out.println(state + " await");
condition.await();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
ReentrantLockCondition2 rlc = new ReentrantLockCondition2();
new Thread(()->rlc.handle(1)).start();
new Thread(()->rlc.handle(2)).start();
new Thread(()->rlc.handle(3)).start();
}
}
方法六: 使用多个Condition
给每个线程不同的condition. 这个和4的区别是, 可以用condition.signal()精确地通知对应的线程继续执行(在对应的condition上await的线程, 可能是多个). 这种情况下是可以多个线程都不unlock锁的情况下进行协作的. 注意下面的while(true)循环是在lock.lock()方法内部的.
public class ReentrantLockCondition {
private static Lock lock = new ReentrantLock();
private static Condition[] conditions = {lock.newCondition(), lock.newCondition(), lock.newCondition()};
private volatile int state = 1;
private void handle(int state) {
lock.lock();
try {
while(true) {
while(this.state != state) {
conditions[state - 1].await();
}
System.out.println(state);
this.state = state % 3 + 1;
conditions[this.state - 1].signal();
conditions[state - 1].await();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
ReentrantLockCondition rlc = new ReentrantLockCondition();
new Thread(()->rlc.handle(1)).start();
new Thread(()->rlc.handle(2)).start();
new Thread(()->rlc.handle(3)).start();
}
}
来源:https://blog.csdn.net/linyixiao88/article/details/81873578


猜你喜欢
- 本文实例讲述了C#中ExecuteNonQuery()返回值注意点。对于C#数据库程序设计有一定的借鉴价值。分享给大家供大家参考之用。具体分
- JVM内存模型/内存空间Java虚拟机JVM运行起来,就会给内存划分空间,这块空间成为运行时数据区。运行时数据区主要划分为以下 6
- 1. 前言今天开始我们来一步步窥探它是如何工作的。我们又该如何驾驭它。本篇将通过 Spring Boot 2.x 来讲解 Spring Se
- 1. 介绍结合上面的ReentrantLock类图,ReentrantLock实现了Lock接口,它的内部类Sync继承自AQS,绝大部分使
- 1、完美1比1 仿照微信仿微信发动态 九宫格拖拽、删除暴力拖拽ui有点问题,不影响使用,资源文件自己找个+号2、微信发动态拖拽bug当选择完
- C#中常涉及到对用户密码的加密于解密的算法,其中使用MD5加密是最常见的的实现方式。本文总结了通用的算法并结合了自己的一点小经验,分享给大家
- 本文实例为大家分享了SeekBar拖动条的应用代码,供大家参考,具体内容如下目标效果在该页面中放一个拖动条的状态提示信息,一个拖动条以及一个
- ForkJoinTask就是ForkJoinPool里面的每一个任务。他主要有两个子类:RecursiveAction和RecursiveT
- pom.xml配置<dependency> <groupId>org.springframework.
- 1. Date.compareTo()java.util.Date提供了在Java中比较两个日期的经典方法compareTo()。如果两个日
- 接上一篇写的截取电脑屏幕,我们在原来的基础上加一个选择区域的功能,实现自定义选择截图。个人比较懒,上一篇的代码就不重新设计了,就简单改一下呈
- 一,Maven 依赖 pom.xml配置1, 去掉默认日志,以便切换到log4j2的日志依赖2, 然后添加如下两个日志依赖二,在工程根目录下
- 在C#中 “\”是特殊字符,要表示它的话需要使用“\\”。由于这种写法不方便,C#语言提供了@对其简化。只要在字符串前加上@即可直接使用“\
- 引言Java * 机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。代理
- 前言目前,企业项目的开发过程中,往往会使用配置文件来做一些配置项来实现项目部署的灵活性,避免硬编码的方式在环境变化时需要对代码进行重新编译。
- 标识符Java 对各种变量、方法和类等要素命名时使用的字符序列称为标识符技巧:凡是自己可以起名字的地方都叫标识符定义合法标识符规则:由26个
- 本文实例为大家分享了android实现录屏小功能的具体代码,供大家参考,具体内容如下思路android实现录屏功能有两种方案,一种是直接使用
- 映射匹配兼容性前面我们已经能从表中查询出数据,并将数据封装到模型类中,这整个过程涉及到一张表和一个模型类:之所以数据能够成功的从表中获取并封
- 多环境开发我们平常都是在自己的开发环境进行开发,当开发完成后,需要把开发的功能部署到测试环境供测试人员进行测试使用,等测试人员测试通过后,我
- 前言static和final是两个我们必须掌握的关键字。不同于其他关键字,他们都有多种用法,而且在一定环境下使用,可以提高程序的运行性能,优