Java 多线程交替打印
目录
题目
使用三个线程 T1、T2、T3,如何让他们按顺序交替打印 10 次 A B C。
方案一:synchronized
public class SynchronizedLockPrinter {
static class Printer {
private final Object lock = new Object();
private int count = 0;
public void print(int n, int target, char content) {
for (int i = 0; i < n; ) {
synchronized (lock) {
while (count % 3 != target) {
try {
lock.wait();
} catch (Exception e) {
System.out.println(e);
}
}
System.out.print(content);
count++;
i++;
lock.notifyAll();
}
}
}
public void print() {
new Thread(()-> {print(10, 0, 'A');}).start();
new Thread(()-> {print(10, 1, 'B');}).start();
new Thread(()-> {print(10, 2, 'C');}).start();
}
}
public static void main(String [] args) {
new Printer().print();
}
}
注意,这里需要通过 volatile 关键自
方法二:ReentrantLock
public class SignalLockPrinter {
static class Printer {
private final Lock lock = new ReentrantLock();
private volatile int count = 0;
public void print(int n, int target, char content) {
for (int i = 0; i < n; ) {
lock.lock();
try {
while (count % 3 == target) {
System.out.print(content);
count++;
i++;
}
} catch (Exception e) {
System.out.println(e);
} finally {
lock.unlock();
}
}
}
public void print() {
new Thread(()-> {print(10, 0, 'A');}).start();
new Thread(()-> {print(10, 1, 'B');}).start();
new Thread(()-> {print(10, 2, 'C');}).start();
}
}
public static void main(String [] args) {
new Printer().print();
}
}
方法三:ReentrantLock + Condition(非公平锁)
public class UnfairLockConditionPrinter {
static class ConditionPrinter {
private final Lock lock = new ReentrantLock(); // 非公平锁
private volatile int count = 0;
private final int threadNumber = 10;
private final Condition condition1 = lock.newCondition();
private final Condition condition2 = lock.newCondition();
private final Condition condition3 = lock.newCondition();
public void print(int target, String content, Condition current, Condition next) {
for (int i = 0; i < threadNumber; i++) {
lock.lock();
try {
// 执行临界区代码前判断:防止锁被不满足条件的线程抢占
while (count % 3 != target) {
current.await(); // 条件等待并释放锁
}
System.out.print(content);
count++; // 注意:这不是一个原子操作
next.signal(); // 唤醒一个等待该条件的线程
} catch (Exception e) {
System.out.println(e);
} finally {
lock.unlock();
}
}
}
public void print() {
new Thread(() -> {print(0, "A", condition1, condition2);}).start();
new Thread(() -> {print(1, "B", condition2, condition3);}).start();
new Thread(() -> {print(2, "C", condition3, condition1);}).start();
}
}
public static void main(String[] args) {
new ConditionPrinter().print();
}
}
注意,
-
unlock()
并不会阻塞当前线程,所以,当 A 线程在释放锁后,线程状态并没有改变,所以 A 线程还会去尝试获取一次锁,如果获取锁失败,就会进入阻塞状态;如果获取成功,就会进入条件等待状态。 -
count++
不是一个原子操作,但是由于同时只有一个线程在执行,所以结果是正确的。实际使用的时候,可以替换为
LongAdder
、AtomicInteger
等原子类。
优化:改成公平锁,可以减少锁的竞争程度。
方法四:ReentrantLock + Condition(公平锁)
public class FairLockConditionPrinter {
static class ConditionPrinterEnhance {
private final Lock lock = new ReentrantLock(true);
private final Condition condition = lock.newCondition();
private volatile int count = 0;
private final int threadNumber = 10;
public void print(int target, char content) {
lock.lock();
try {
for (int i = 0; i < threadNumber; i++) {
while (count % 3 != target) {
condition.await();
}
System.out.println(Thread.currentThread().getName() + ": " + content + " " + count);
count++;
condition.signal();
}
} catch (Exception e) {
System.out.println(e);
} finally {
lock.unlock();
}
}
public void print() {
char content = 'A';
for (int i = 0; i < 3; i++) {
final int k = i;
new Thread(() -> print( k, (char) (content + k))).start();
}
}
}
public static void main(String[] args) {
new ConditionPrinterEnhance().print();
}
}
或者
public class Test {
private static Lock lock = new ReentrantLock();
private static Condition c1 = lock.newCondition();
private static Condition c2 = lock.newCondition();
private static Condition c3 = lock.newCondition();
private void printABC(Condition currentThread, Condition nextThread) {
for (int i = 0; i < 10; i++) {
lock.lock();
try {
System.out.print(Thread.currentThread().getName());
nextThread.signal(); //唤醒下一个线程,而不是唤醒所有线程
currentThread.await(); // 可以在最后一个线程执行的时候,跳过不等待,避免最后一个线程执行完后挂起
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
Test print = new Test();
new Thread(() -> {
print.printABC(c1, c2);
}, "A").start();
new Thread(() -> {
print.printABC(c2, c3);
}, "B").start();
new Thread(() -> {
print.printABC(c3, c1);
}, "C").start();
}
}
方法五:Semaphore
public class SemaphorePrinter {
static class Printer {
private final int n = 10;
private final Semaphore s1 = new Semaphore(1);
private final Semaphore s2 = new Semaphore(0);
private final Semaphore s3 = new Semaphore(0);
private void print(char content, Semaphore current, Semaphore next) {
for (int i = 0; i < n; i++) {
try {
current.acquire();
System.out.print(content);
next.release();
} catch (Exception e) {
System.out.println(e);
}
}
}
public void print() {
new Thread(() -> {print('A', s1, s2);}).start();
new Thread(() -> {print('B', s2, s3);}).start();
new Thread(() -> {print('C', s3, s1);}).start();
}
}
public static void main(String [] args) {
new Printer().print();
}
}