线程类:
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();
}