如何让两个线程交替打印数字
问题
如何让两个线程交替打印 1-100 的数字? 废话不多说, 直接上代码:
synchronized 锁 + AtomicIntegerimportjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
importjava.util.concurrent.atomic.AtomicInteger;
publicclassStrangePrinter{
privateintmax;
privateAtomicIntegerstatus=newAtomicInteger(1);// AtomicInteger 保证可见性, 也可以用 volatile
publicStrangePrinter(intmax){
this.max=max;
}
publicstaticvoidmain(String[]args){
StrangePrinterstrangePrinter=newStrangePrinter(100);
ExecutorServiceexecutorService=Executors.newFixedThreadPool(2);
executorService.submit(strangePrinter.newMyPrinter("Print1",0));
executorService.submit(strangePrinter.newMyPrinter("Print2",1));
executorService.shutdown();
}
classMyPrinterimplementsRunnable{
privateStringname;
privateinttype;// 打印的类型, 0: 代表打印奇数, 1: 代表打印偶数
publicMyPrinter(Stringname,inttype){
this.name=name;
this.type=type;
}
@Override
publicvoidrun(){
if(type==1){
while(status.get()<=max){
synchronized(StrangePrinter.class){// 加锁, 保证下面的操作是一个原子操作
// 打印偶数
if(status.get()<=max&&status.get()%2==0){// 打印偶数
System.out.println(name+"-"+status.getAndIncrement());
}
}
}
}else{
while(status.get()<=max){
synchronized(StrangePrinter.class){// 加锁
// 打印奇数
if(status.get()<=max&&status.get()%2!=0){// 打印奇数
System.out.println(name+"-"+status.getAndIncrement());
}
}
}
}
}
}
}
这里需要注意两点:
用 AtomicInteger 保证多线程数据可见性.
不要觉得 synchronized 加锁是多余的, 如果没有加锁, 线程 1 和线程 2 就可能出现不是交替打印的情况. 如果没有加锁, 设想线程 1 打印完了一个奇数后, 线程 2 去打印下一个偶数, 当执行完
status.getAndIncrement()
后, 此时 status 又是奇数了, 当此时 CPU 将线程 2 挂起, 调度线程 1, 就会出现线程 2 还没来得及打印偶数, 线程 1 就已经打印了下一个奇数的情况. 就不符合题目要求了. 因此这里加锁是必须的, 保证代码块中的是一个原子操作.
使用 Object 的 wait 和 notify 实现importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
importjava.util.concurrent.atomic.AtomicInteger;
publicclassStrangePrinter2{
Objectodd=newObject();// 奇数条件锁
Objecteven=newObject();// 偶数条件锁
privateintmax;
privateAtomicIntegerstatus=newAtomicInteger(1);// AtomicInteger 保证可见性, 也可以用 volatile
publicStrangePrinter2(intmax){
this.max=max;
}
publicstaticvoidmain(String[]args){
StrangePrinter2strangePrinter=newStrangePrinter2(100);
ExecutorServiceexecutorService=Executors.newFixedThreadPool(2);
executorService.submit(strangePrinter.newMyPrinter("偶数 Printer",0));
executorService.submit(strangePrinter.newMyPrinter("奇数 Printer",1));
executorService.shutdown();
}
classMyPrinterimplementsRunnable{
privateStringname;
privateinttype;// 打印的类型, 0: 代表打印奇数, 1: 代表打印偶数
publicMyPrinter(Stringname,inttype){
this.name=name;
this.type=type;
}
@Override
publicvoidrun(){
if(type==1){
while(status.get()<=max){// 打印奇数
if(status.get()%2==0){// 如果当前为偶数, 则等待
synchronized(odd){
try{
odd.wait();
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
}else{
System.out.println(name+"-"+status.getAndIncrement());// 打印奇数
synchronized(even){// 通知偶数打印线程
even.notify();
}
}
}
}else{
while(status.get()<=max){// 打印偶数
if(status.get()%2!=0){// 如果当前为奇数, 则等待
synchronized(even){
try{
even.wait();
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
}else{
System.out.println(name+"-"+status.getAndIncrement());// 打印偶数
synchronized(odd){// 通知奇数打印线程
odd.notify();
}
}
}
}
}
}
}
使用 ReentrantLock+Condition 实现importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
importjava.util.concurrent.atomic.AtomicInteger;
importjava.util.concurrent.locks.Condition;
importjava.util.concurrent.locks.ReentrantLock;
publicclassStrangePrinter3{
privateintmax;
privateAtomicIntegerstatus=newAtomicInteger(1);// AtomicInteger 保证可见性, 也可以用 volatile
privateReentrantLocklock=newReentrantLock();
privateConditionodd=lock.newCondition();
privateConditioneven=lock.newCondition();
publicStrangePrinter3(intmax){
this.max=max;
}
publicstaticvoidmain(String[]args){
StrangePrinter3strangePrinter=newStrangePrinter3(100);
ExecutorServiceexecutorService=Executors.newFixedThreadPool(2);
executorService.submit(strangePrinter.newMyPrinter("偶数 Printer",0));
executorService.submit(strangePrinter.newMyPrinter("奇数 Printer",1));
executorService.shutdown();
}
classMyPrinterimplementsRunnable{
privateStringname;
privateinttype;// 打印的类型, 0: 代表打印奇数, 1: 代表打印偶数
publicMyPrinter(Stringname,inttype){
this.name=name;
this.type=type;
}
@Override
publicvoidrun(){
if(type==1){
while(status.get()<=max){// 打印奇数
lock.lock();
try{
if(status.get()%2==0){
odd.await();
}
if(status.get()<=max){
System.out.println(name+"-"+status.getAndIncrement());// 打印奇数
}
even.signal();
}catch(InterruptedExceptione){
e.printStackTrace();
}finally{
lock.unlock();
}
}
}else{
while(status.get()<=max){// 打印偶数
lock.lock();
try{
if(status.get()%2!=0){
even.await();
}
if(status.get()<=max){
System.out.println(name+"-"+status.getAndIncrement());// 打印偶数
}
odd.signal();
}catch(InterruptedExceptione){
e.printStackTrace();
}finally{
lock.unlock();
}
}
}
}
}
}
这里的实现思路其实和使用 Object 的 wait 和 notify 机制差不多.
通过 flag 标识实现importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
importjava.util.concurrent.atomic.AtomicInteger;
publicclassStrangePrinter4{
privateintmax;
privateAtomicIntegerstatus=newAtomicInteger(1);// AtomicInteger 保证可见性, 也可以用 volatile
privatebooleanoddFlag=true;
publicStrangePrinter4(intmax){
this.max=max;
}
publicstaticvoidmain(String[]args){
StrangePrinter4strangePrinter=newStrangePrinter4(100);
ExecutorServiceexecutorService=Executors.newFixedThreadPool(2);
executorService.submit(strangePrinter.newMyPrinter("偶数 Printer",0));
executorService.submit(strangePrinter.newMyPrinter("奇数 Printer",1));
executorService.shutdown();
}
classMyPrinterimplementsRunnable{
privateStringname;
privateinttype;// 打印的类型, 0: 代表打印奇数, 1: 代表打印偶数
publicMyPrinter(Stringname,inttype){
this.name=name;
this.type=type;
}
@Override
publicvoidrun(){
if(type==1){
while(status.get()<=max){// 打印奇数
if(oddFlag){
System.out.println(name+"-"+status.getAndIncrement());// 打印奇数
oddFlag=!oddFlag;
}
}
}else{
while(status.get()<=max){// 打印偶数
if(!oddFlag){
System.out.println(name+"-"+status.getAndIncrement());// 打印奇数
oddFlag=!oddFlag;
}
}
}
}
}
}
这是最简单最高效的实现方式, 因为不需要加锁. 比前面两种实现方式都要好一些.
来源: https://juejin.im/post/5c728e8651882562c85ae4f1