循环打印ABC问题
题目:有三个线程分别打印A、B、C,请用多线程编程实现,在屏幕打印10次ABC
整体思路:该问题为三个线程的同步唤醒机制即ThreadA->ThreadB->ThreadC->ThreadA循环执行三个线程。
synchronized+wait/notify
基本思路就是线程A、线程B、线程C三个线程同时启动,因为变量num的初始值为0,所以线程B或线程C拿到锁后,进入while()循环,然后执行wait()方法,线程线程阻塞,释放锁。只有线程A拿到锁后,不进入while()循环,执行num++,打印字符A,最后唤醒线程B和线程C。此时num值为1,只有线程B拿到锁后,不被阻塞,执行num++,打印字符B,最后唤醒线程A和线程C,后面以此类推。
wait,notify都类似condition,不符合就一直wait,然后要notifyAll,因为可能多个等,一个线程可能持续等多次,然后没notify。
class Wait_Notify_ACB {
private int num;
private static final Object LOCK = new Object();
private void printABC(int targetNum) {
for (int i = 0; i < 100; i++) {
synchronized (LOCK) {
while (num % 3 != targetNum) {
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num++;
System.out.print(Thread.currentThread().getName());
LOCK.notifyAll();
}
}
}
public static void main(String[] args) {
Wait_Notify_ACB wait_notify_acb = new Wait_Notify_ACB();
new Thread(() -> {
wait_notify_acb.printABC(0);
}, "A").start();
new Thread(() -> {
wait_notify_acb.printABC(1);
}, "B").start();
new Thread(() -> {
wait_notify_acb.printABC(2);
}, "C").start();
}
}
Semaphore
信号量,类似golang的channel来实现。0长度的阻塞队列
class SemaphoreABC {
private static Semaphore s1 = new Semaphore(1); //因为先执行线程A,所以这里设s1的计数器为1
private static Semaphore s2 = new Semaphore(0);
private static Semaphore s3 = new Semaphore(0);
private void printABC(Semaphore currentThread, Semaphore nextThread) {
for (int i = 0; i < 10; i++) {
try {
currentThread.acquire(); //阻塞当前线程,即信号量的计数器减1为0
System.out.print(Thread.currentThread().getName());
nextThread.release(); //唤醒下一个线程,即信号量的计数器加1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
SemaphoreABC printer = new SemaphoreABC();
new Thread(() -> {
printer.printABC(s1, s2);
}, "A").start();
Thread.sleep(10);
new Thread(() -> {
printer.printABC(s2, s3);
}, "B").start();
Thread.sleep(10);
new Thread(() -> {
printer.printABC(s3, s1);
}, "C").start();
}
}
ReentrantLock
重入锁。一个condition. condition的signalAll唤醒了所有的,但其实不是所有的都需要唤醒,可以拆成3个,在后续
class Wait_Notify_ACB {
private int num;
private static final Lock lock = new ReentrantLock();
private static final Condition condition = lock.newCondition();
private void printABC(int targetNum) {
for (int i = 0; i < 100; i++) {
lock.lock();
try {
while (num % 3 != targetNum) {
condition.await();
}
num++;
System.out.print(Thread.currentThread().getName());
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
condition.signalAll();
lock.unlock();
}
}
}
public static void main(String[] args) {
Wait_Notify_ACB wait_notify_acb = new Wait_Notify_ACB();
new Thread(() -> {
wait_notify_acb.printABC(0);
}, "A").start();
new Thread(() -> {
wait_notify_acb.printABC(1);
}, "B").start();
new Thread(() -> {
wait_notify_acb.printABC(2);
}, "C").start();
}
}
Reentrantlock + 3个condition
这里没维护变量,因此3个condition是分开的。也可以维护变量来判断。唤醒只用signal是因为等待的线程不会超过1个。
class Solution {
static Lock lock = new ReentrantLock();
Condition pre, nxt;
String val;
Solution(Condition pre, Condition nxt, String val) {
this.pre = pre;
this.nxt = nxt;
this.val = val;
}
void run() {
for (int i = 0; i < 100; i++) {
lock.lock();
try {
pre.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.print(val);
nxt.signal();
lock.unlock();
}
}
public static void main(String[] args) {
Condition a = lock.newCondition();
Condition b = lock.newCondition();
Condition c = lock.newCondition();
new Thread(() -> {
new Solution(a, b, "A").run();
}).start();
new Thread(() -> {
new Solution(b, c, "B").run();
}).start();
new Thread(() -> {
new Solution(c, a, "C").run();
}).start();
lock.lock();
try {
a.signal();
} finally {
lock.unlock();
}
}
}
CyclicBarrier
每次到达一个点,就开始判断打印哪个字母,然后接着往后移动。推荐
class Solution {
static Integer state = 0;
static Map<Integer, String> map = Map.of(0, "A", 1, "B", 2, "C");
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
int t = state % 3;
System.out.print(map.get(t));
state += 1;
});
Runnable runnable = () -> {
for (int i = 0; i < 300; i++) {
try {
barrier.await();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
};
new Thread(runnable).start();
new Thread(runnable).start();
new Thread(runnable).start();
}
}
Option2. 线程1 有3个cyclicBarrier, 线程2有两个,线程3有3个。
class Solution {
static Integer state = 0;
static Map<Integer, String> map = Map.of(0, "A", 1, "B", 2, "C");
public static void main(String[] args) {
CyclicBarrier b1 = new CyclicBarrier(1, () -> {
System.out.print("A");
});
CyclicBarrier b2 = new CyclicBarrier(2, () -> {
System.out.print("B");
});
CyclicBarrier b3 = new CyclicBarrier(3, () -> {
System.out.print("C");
});
new Thread(() -> {
try {
for (int i = 0; i < 100; i++) {
b1.await();
b2.await();
b3.await();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}).start();
new Thread(() -> {
try {
for (int i = 0; i < 100; i++) {
b2.await();
b3.await();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}).start();
new Thread(() -> {
try {
for (int i = 0; i < 100; i++) {
b3.await();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}).start();
}
}
阻塞队列
class Handler implements Runnable {
final SynchronousQueue<Integer> pre, nxt;
final String val;
final boolean last;
Handler(SynchronousQueue<Integer> pre, SynchronousQueue<Integer> nxt, String val, boolean last) {
this.pre = pre;
this.nxt = nxt;
this.val = val;
this.last = last;
}
public void run() {
for (int i = 0; i < 100; i++) {
try {
this.pre.take();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.print(this.val + i);
if (this.last && i == 99) {
break;
}
this.nxt.offer(0);
}
}
}
class Solution {
public static void main(String[] args) throws InterruptedException {
SynchronousQueue<Integer> a = new SynchronousQueue();
SynchronousQueue<Integer> b = new SynchronousQueue();
SynchronousQueue<Integer> c = new SynchronousQueue();
new Thread(new Handler(a, b, "A", false)).start();
new Thread(new Handler(b, c, "B", false)).start();
new Thread(new Handler(c, a, "C", true)).start();
a.put(0);
Thread.sleep(1000);
}
}
生产者消费者
模拟顾客,厨师,服务员。顾客下单给厨师,厨师做好菜给服务员,服务员来上菜。
class Person {
String name = "customer";
}
class Customer extends Person {
public Customer() {
this.name = "customer";
}
}
class Cook extends Person {
}
class Waiter extends Person {
}
class Menu {
final Customer customer;
final List<String> foodNames;
Menu(Customer customer, List<String> foodNames) {
this.customer = customer;
this.foodNames = foodNames;
}
}
class Food {
final Menu menu;
Food(Menu menu) {
this.menu = menu;
}
}
class Solution {
static List<Customer> customers = new ArrayList<>();
static Cook cook = new Cook();
static List<Waiter> waiters = new ArrayList<>();
static BlockingQueue<Menu> orders = new LinkedBlockingQueue<>(10);
static BlockingQueue<Food> foods = new LinkedBlockingQueue<>(10);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
customers.add(new Customer());
}
for (int i = 0; i < 5; i++) {
waiters.add(new Waiter());
}
for (Customer customer : customers) {
new Thread(() -> {
try {
orders.put(new Menu(customer, new ArrayList<>()));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
}
for (Waiter waiter : waiters) {
new Thread(() -> {
while (true) {
try {
Food food = foods.take();
System.out.println(waiter + " 上菜 " + food + " 给顾客 " + food.menu.customer);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start();
}
new Thread(() -> {
while (true) {
try {
Menu menu = orders.take();
Food food = new Food(menu);
System.out.println(cook + " 处理食物");
foods.put(food);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start();
Thread.sleep(1000);
System.out.println("done");
}
}