用三个线程按顺序循环打印abc 三个字母,比如abcabcabc

线程类:

import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; /** * Created by Andrewon 2017/3/22. * update on 2019/12/08 unlock规范化,默认不限制打印次数 */ public class PrintThread implements Runnable{ private String symbol; private Condition conditionA =null; private int go = 0; private ReentrantLock lock =null; //使用原子类,本例中并没有多大意义 private static AtomicInteger i=new AtomicInteger(0); public PrintThread(String symbol, Condition conditionA,int go,ReentrantLock lock) { this.symbol = symbol; this.lock = lock; this.conditionA = conditionA; this.go = go; } @Override public void run() { while (true){ lock.lock(); try { try { while (i.get() % 3 != go) { conditionA.await(); } } catch (InterruptedException e) { e.printStackTrace(); } //防止过多输出 //if (i.get()==10)break; System.out.println("result " + symbol); i.getAndIncrement(); //System.out.println(i.get()); //可以试试signalAll或signal区别,比较时建议去掉前面的break conditionA.signalAll(); } finally { lock.unlock(); } } } }

【用三个线程按顺序循环打印abc 三个字母,比如abcabcabc】
main函数:
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; /** * Created by Andrewon 2017/3/22. */ public class PrintLetter { private static ReentrantLock lock = new ReentrantLock(); private static Condition conditionA = lock.newCondition(); public static void main(String[] args) throws InterruptedException { new Thread(new PrintThread("A",conditionA,0,lock)).start(); new Thread(new PrintThread("B",conditionA,1,lock)).start(); new Thread(new PrintThread("C",conditionA,2,lock)).start(); }}

下面这种实现是我最初的想法但当时并没能实现,这种方式我认为比较巧妙但存在两大问题:
1.达到目的后仍有线程处于等待状态。
2.由于运行环境可能达不到最终结果。主要原因是线程调度顺序

/** * Created by Andrewon 2017/3/26. */ public class MyThreadPrinter implements Runnable { private String name; private Object prev; private Object self; private MyThreadPrinter(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) { synchronized (prev) { synchronized (self) { System.out.print(name); count--; self.notify(); } try { prev.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }} }public static void main(String[] args) throws Exception { Object a = new Object(); Object b = new Object(); Object c = new Object(); MyThreadPrinter pa = new MyThreadPrinter("A", c, a); MyThreadPrinter pb = new MyThreadPrinter("B", a, b); MyThreadPrinter pc = new MyThreadPrinter("C", b, c); new Thread(pa).start(); new Thread(pb).start(); new Thread(pc).start(); } }

错误调度顺序:如果主线程在启动A后,执行A,过程中又切回主线程,启动了ThreadB,ThreadC,之后,由于A线程尚未释放self.notify,也就是B需要在synchronized(prev)处等待,而这时C却调用synchronized(prev)获取了对b的对象锁。这样,在A调用完后,同时ThreadB获取了prev也就是a的对象锁,ThreadC的执行条件就已经满足了,会打印C,之后释放c,及b的对象锁,这时ThreadB具备了运行条件,会打印B,也就是循环变成了ACBACB了

最近刚好又接触到了更新一下==========2019/12/08
第一种思路的另一种实现,也可以不加锁依赖原子类同步能力忙等:
public class PrintThreadimplements Runnable{ private String symbol; private int go; private final Object lock; //使用原子类,本例中并没有多大意义 private static AtomicInteger i = new AtomicInteger(0); public PrintThread(String symbol, int go, Object lock) { this.symbol = symbol; this.lock = lock; this.go = go; } @Override public void run() { while (true) { synchronized (lock) { try { while (i.get() % 3 != go) { lock.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } //防止过多输出 //if (i.get()==10)break; System.out.println(Thread.currentThread().getName() + " "+ symbol); i.getAndIncrement(); //System.out.println(i.get()); //可以试试signalAll或signal区别,比较时建议去掉前面的break lock.notifyAll(); } } } }

使用阻塞队列实现:
public class PrintThread implements Runnable { private String symbol; private BlockingQueue messageQueue; private BlockingQueue putQueue; public PrintThread(String symbol, BlockingQueue messageQueue, BlockingQueue putQueue) { this.symbol = symbol; this.messageQueue = messageQueue; this.putQueue = putQueue; }@Override public void run() { while (true) { String message = null; try { message = messageQueue.take(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " "+ message); putQueue.add(symbol); } } }

初始化方法,还可以通过初始化参数调整打印结果:
public static void main(String[] args) { BlockingQueue messageQueueA = new ArrayBlockingQueue<>(1); BlockingQueue messageQueueB = new ArrayBlockingQueue<>(1); BlockingQueue messageQueueC = new ArrayBlockingQueue<>(1); messageQueueA.add("A"); new Thread(new PrintThread("B", messageQueueA, messageQueueB)).start(); new Thread(new PrintThread("C", messageQueueB, messageQueueC)).start(); new Thread(new PrintThread("A", messageQueueC, messageQueueA)).start(); }

单个阻塞队列实现,线程忙等浪费CPU资源:
public class PrintThread implements Runnable { private String symbol; private String putSymbol; private BlockingQueue queue; public PrintThread(String symbol, String putSymbol, BlockingQueue queue) { this.symbol = symbol; this.putSymbol = putSymbol; this.queue = queue; }@Override public void run() { while (true) { while (!symbol.equals(queue.peek())); queue.remove(); System.out.println(Thread.currentThread().getName() + " "+ symbol); queue.add(putSymbol); } } }

初始化方法:
public static void main(String[] args) { BlockingQueue messageQueue = new ArrayBlockingQueue<>(1); messageQueue.add("A"); new Thread(new PrintThread("A", "B", messageQueue)).start(); new Thread(new PrintThread("B", "C", messageQueue)).start(); new Thread(new PrintThread("C", "A", messageQueue)).start(); }


    推荐阅读