三个线程,分别输出 a 、b 、c,各输出 100 次,要求按 abc 的顺序输出
期待输出为:abcabcabcabc...
1
Yi23 2021-01-25 12:47:01 +08:00
第一反应可以使用 lock.condition 实现,3 个线程每个线程打印自己的字符,然后唤醒下一个线程。
线程 1 的方法类似如下 类似这样 ``` lock.lock(); while (n != a) { condition1.await(); // 线程 1 阻塞 } // 输出 n n = b; // 修改 n=b 然后唤醒线程 2 condition2.signal(); lock.unlock(); ``` |
2
chenshun00 2021-01-25 13:00:29 +08:00
public static void main(String[] args) {
final Object xx1 = new Object(); final Object xx2 = new Object(); final Object xx3 = new Object(); Thread a = new Thread(() -> { try { synchronized (xx1) { xx1.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } int a1 = 0; while (a1 < 100) { System.out.print("a"); a1++; synchronized (xx2) { xx2.notify(); } try { synchronized (xx1) { xx1.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } } }); a.setDaemon(true); a.setName("aaa"); Thread b = new Thread(() -> { try { synchronized (xx2) { xx2.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } int b1 = 0; while (b1 < 100) { System.out.print("b"); b1++; synchronized (xx3) { xx3.notify(); } try { synchronized (xx2) { xx2.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } } }); b.setDaemon(true); b.setName("bbb"); Thread c = new Thread(() -> { try { synchronized (xx3) { xx3.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } int c1 = 0; while (c1 < 100) { System.out.println("c"); c1++; synchronized (xx1) { xx1.notify(); } try { synchronized (xx3) { xx3.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } } }); c.setDaemon(true); c.setName("ccc"); a.start(); b.start(); c.start(); synchronized (xx1) { xx1.notify(); } try { Thread.sleep(100L); } catch (InterruptedException e) { e.printStackTrace(); } } } |
3
mawerss1 2021-01-25 13:06:56 +08:00
public class Volatile implements Runnable{
private volatile int state = 0; public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 3; i++) { Thread thread = new Thread(new Volatile()); thread.start(); thread.join(); } } @Override public void run() { int count = 0; while (true) { if (count >= 100) { break; } if (state == 0) { System.out.println("a"); state = 1; } if (state == 1) { System.out.println("b"); state = 2; } if (state == 2) { System.out.println("c"); state = 0; } count++; } } } |
4
chenshun00 2021-01-25 13:12:25 +08:00
@mawerss1 好像偏题了
|
5
mytudan 2021-01-25 13:26:22 +08:00
class Abc {
private int n; // 标志位,控制执行顺序,0 执行 a,1 执行 b 2 执行 c private volatile int type; // 锁标志 private final Object object; public Abc(int n) { this.n = n; object = new Object(); type = 0; } public void a(Runnable a) throws InterruptedException { for (int i = 0; i < n; i++) { synchronized (object) { while (!(type == 0)) { object.wait(); } a.run(); type = 1; object.notifyAll(); } } } public void b(Runnable b) throws InterruptedException { for (int i = 0; i < n; i++) { synchronized (object) { while (!(type == 1)) { object.wait(); } b.run(); type = 2; object.notifyAll(); } } } public void c(Runnable c) throws InterruptedException { for (int i = 0; i < n; i++) { synchronized (object) { while (!(type == 2)) { object.wait(); } c.run(); type = 0; object.notifyAll(); } } } } |
6
mawerss1 2021-01-25 13:34:29 +08:00
@chenshun00 哈哈 全错了
|
7
chendy 2021-01-25 13:35:06 +08:00
生产者-消费者模型
a 生产 b 消费,b 生产 c 消费,c 生产 a 消费 |
8
leafre 2021-01-25 13:41:17 +08:00
ReentrantLock
|
9
hitmanx 2021-01-25 13:46:59 +08:00
把它看成三个独立的生产者消费者,用独立的 sync objects
|
10
woshiaha 2021-01-25 13:49:04 +08:00 1
老题了 用一个原子数当锁 每次有输出原子数都加一 抢到锁才可以输出
a 线程只在原子数%3 为 0 时输出 b 线程只在原子数%3 为 1 时输出 c 线程只在原子数%3 为 2 时输出 |
11
mytudan 2021-01-25 13:49:26 +08:00
我上面那个是照着 leetcode 上的题改的
|
12
mawerss1 2021-01-25 13:51:31 +08:00
@chenshun00 state 变量改成 static,join 方法删掉,改成用 CountDownLatch
|
13
yazinnnn 2021-01-25 13:56:11 +08:00 2
有个问题,这个多线程有什么意义?
|
14
Vendettar 2021-01-25 14:09:30 +08:00
```java
public static void main(String[] args) { ReentrantLock lock = new ReentrantLock(true); for (int i = 0; i < 3; i++) { int order = i; new Thread(()->{ for (int j = 0; j < 100; j++) { lock.lock(); System.out.println(Thread.currentThread().getName() + ":" + (char)('a'+order)); lock.unlock(); } }, "Thread-" + i).start(); } } ``` |
15
Vendettar 2021-01-25 14:10:16 +08:00
直接一个公平锁 new ReentrantLock(true)按顺序来就可以
|
16
Takamine 2021-01-25 14:13:50 +08:00 via Android
刚撸的一个,应该算是比较传统的解法吧。
https://paste.ubuntu.com/p/zbDMK6qJQm/ |
17
micean 2021-01-25 14:14:46 +08:00
每个线程调用这个类就行了
class ABCPrinter{ int[] arr = new int[]{0, 0, 0}; int i = 0; public synchronized void addChar(char ch){ arr[(int)(ch - 'a')]++; print(); } void print(){ while(arr[i] > 0){ 打印((char)(arr[i] + 'a')); arr[i]--; i = (i + 1)%3 } } } |
19
Rorysky 2021-01-25 14:19:28 +08:00
synchronized 一把梭
|
24
ocean1477 2021-01-25 14:29:29 +08:00
@Vendettar 我这会试了下,会乱的,多试几次。
Thread-0:a Thread-0:a Thread-0:a Thread-0:a Thread-0:a Thread-1:b Thread-2:c Thread-0:a Thread-1:b Thread-2:c Thread-0:a Thread-1:b Thread-2:c Thread-0:a Thread-1:b |
28
kx5d62Jn1J9MjoXP 2021-01-25 14:39:16 +08:00
用三个 Semaphore 就行了吧
|
30
sczero 2021-01-25 14:43:56 +08:00
public static void main(String[] args) throws InterruptedException {
final AtomicReference<String> tag = new AtomicReference<>("a"); final int total = 100; new Thread(() -> { int count = 0; while (count < total) { if (tag.get().equals("a")) { System.out.print("a"); tag.set("b"); count++; } } }).start(); new Thread(() -> { int count = 0; while (count < total) { if (tag.get().equals("b")) { System.out.print("b"); tag.set("c"); count++; } } }).start(); new Thread(() -> { int count = 0; while (count < total) { if (tag.get().equals("c")) { System.out.println("c"); tag.set("a"); count++; } } }).start(); } |
31
kx5d62Jn1J9MjoXP 2021-01-25 14:52:58 +08:00 1
public static void main(String[] args) {
Semaphore[] res = { new Semaphore(1), new Semaphore(0), new Semaphore(0), }; for (int i = 0; i < 3; i++) { Semaphore current = res[i]; Semaphore next = res[(i + 1) % 3]; final char ch = (char) (i + 'a'); new Thread(new Runnable() { @Override public void run() { for (int j = 0; j < 100; j++) { try { current.acquire(); System.out.print(ch); next.release(); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } } |
32
Cyron 2021-01-25 15:01:38 +08:00
简单公平锁实现
``` public class ReentrantLockDemo implements Runnable { private static ReentrantLock lock = new ReentrantLock(true); private String content; public ReentrantLockDemo(String content) { this.content = content; } @Override public void run() { while (true) { try { lock.lock(); System.out.println(content); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } public static void main(String[] args) { ReentrantLockDemo a = new ReentrantLockDemo("a"); ReentrantLockDemo b = new ReentrantLockDemo("b"); ReentrantLockDemo c = new ReentrantLockDemo("c"); Thread threadA = new Thread(a); Thread threadB = new Thread(b); Thread threadC = new Thread(c); threadA.start(); threadB.start(); threadC.start(); } } ``` |
33
zzh7982 2021-01-25 15:10:55 +08:00
|
34
kikione 2021-01-25 15:17:07 +08:00
我想到两个方案,第一个是加锁。 第二个是 join,b 等 a ,c 等 a 。
|
37
kikione 2021-01-25 15:26:17 +08:00
for (int i=0;i<100;i++){
Thread1 thread1 = new Thread1(); Thread2 thread2 = new Thread2(); Thread3 thread3 = new Thread3(); thread1.start(); thread1.join(); thread2.start(); thread2.join(); thread3.start(); } |
38
sampeng 2021-01-25 15:34:23 +08:00 1
都是有锁方案啊。。我提一个无锁和竞争的方案
三个队列。分别收 a,b,c 。 输出就是轮询这 3 个队列就好了。。一定都是有序的 |
39
johnson666 2021-01-25 16:05:58 +08:00
原子变量
public class Test { public static void main(String[] args) { AtomicInteger at = new AtomicInteger(); for (int i = 0; i < 3; i++) { MyThread t = new MyThread(at, i); t.start(); } } } class MyThread extends Thread { private AtomicInteger at; private int index; public MyThread(AtomicInteger at, int index) { this.at = at; this.index = index; } @Override public void run() { while(true) { if(at.get() % 3 == index) { System.out.print((char)('a' + index)); at.incrementAndGet(); } } } } |
40
Youen 2021-01-25 16:53:22 +08:00
|
41
YoongRii 2021-01-25 17:14:22 +08:00
用原子变量%3 这种解法比较常见,但是需要线程忙等,提供一种用线程池方式实现的思路:
public static void main(String[] args) throws InterruptedException { ExecutorService executorService1 = Executors.newSingleThreadExecutor(); ExecutorService executorService2 = Executors.newSingleThreadExecutor(); ExecutorService executorService3 = Executors.newSingleThreadExecutor(); Runnable[] rs = new Runnable[3]; rs[0] = () -> { System.out.println("A"); executorService2.submit(rs[1]); }; rs[1] = () -> { System.out.println("B"); executorService2.submit(rs[2]); }; rs[2] = new Runnable() { private int a = 1; public void run() { System.out.println("C"); if (a++ < 100) executorService3.submit(rs[0]); } }; executorService1.submit(rs[0]); } |
42
donggexiongdi 2021-01-25 17:19:22 +08:00
public void printAbc(int count) {
for (int i = 0; i < count; i++) { CompletableFuture.runAsync(() -> { System.out.println("A"); }).thenRun(() -> { System.out.println("B"); }).thenRun(() -> { System.out.println("C"); }); System.out.println("-------"); } } |
43
donggexiongdi 2021-01-25 17:22:06 +08:00
我擦 不对
|
44
cubecube 2021-01-26 01:26:51 +08:00
@sampeng
1.队列也需要锁,无锁队列的话,如果某个线程输入太慢,跟不上,consumer 也得等待? 2.另外,题目说得三个线程个,consumer 还得偷线程或者揉进到生产者线程的执行过程中。 以上条件要全写对,比状态量的复杂。 |
45
sampeng 2021-01-26 09:34:19 +08:00
@cubecube
1.我说的无所锁方案是代码不需要明确的写锁的逻辑。也是一个比较自然的做法。而且队列的这种方式并没有资源竞争,只是 consumer 等待而已。 2.三个线程难道就不能有主线程了?谁起的这 3 个线程呢?当然是主线程上直接循环啊。又不是生产环境。 |
46
Vendettar 2021-01-26 11:04:01 +08:00
@Cyron 三个线程首次启动抢锁的话(3 线程都没有 sleep 过),a 抢 1b 抢 2 就没问题,如果 b 抢 1 那后面 99 次都是 b 先输出了
|
47
fantastM 2021-01-27 14:30:52 +08:00
|