Java多线程:用三个线程控制循环顺序输出10次ABC

题目:有A,B,C三个线程, A线程输出A, B线程输出B, C线程输出C,要求, 同时启动三个线程, 按顺序输出ABC, 循环10次。

解题思路:要按顺序输出ABC, 循环10次,就要控制三个线程同步工作,也就是说要让三个线程轮流输出,直到10个ABC全部输出则结束线程。这里用一个Lock对象来控制三个线程的同步。用一个int型变量state标识由那个线程输出。

  1. package com.thread;  
  2.   
  3. import java.util.concurrent.locks.Lock;  
  4. import java.util.concurrent.locks.ReentrantLock;  
  5.   
  6. public class ABC {  
  7.     private static int state = 0;  
  8.   
  9.     public static void main(String[] args) {  
  10.         final Lock l = new ReentrantLock();  
  11.           
  12.         Thread A = new Thread(new Runnable(){  
  13.             @Override  
  14.             public void run() {  
  15.                 while (state<=30) {  
  16.                     l.lock();  
  17.                     if(state%3==0){  
  18.                         System.out.println("A");  
  19.                         state ++;  
  20.                     }  
  21.                     l.unlock();  
  22.                 }  
  23.             }  
  24.         });  
  25.         Thread B = new Thread(new Runnable(){  
  26.             @Override  
  27.             public void run() {  
  28.                 while (state<=30) {  
  29.                     l.lock();  
  30.                     if(state%3==1){  
  31.                         System.out.println("B");  
  32.                         state ++;  
  33.                     }  
  34.                     l.unlock();  
  35.                 }  
  36.             }  
  37.         });  
  38.         Thread C = new Thread(new Runnable(){  
  39.             @Override  
  40.             public void run() {  
  41.                 while (state<=30) {  
  42.                     l.lock();  
  43.                     if(state%3==2){  
  44.                         System.out.println("C");  
  45.                         state ++;  
  46.                     }  
  47.                     l.unlock();  
  48.                 }  
  49.             }  
  50.         });  
  51.         A.start();  
  52.         B.start();  
  53.         C.start();  
  54.     }  
  55.   
  56. }  
  1. package com.thread;  
  2.   
  3. import java.util.concurrent.locks.Lock;  
  4. import java.util.concurrent.locks.ReentrantLock;  
  5.   
  6. public class ABC {  
  7.     private static int state = 0;  
  8.   
  9.     public static void main(String[] args) {  
  10.         final Lock l = new ReentrantLock();  
  11.           
  12.         Thread A = new Thread(new Runnable(){  
  13.             @Override  
  14.             public void run() {  
  15.                 while (state<=30) {  
  16.                     l.lock();  
  17.                     if(state%3==0){  
  18.                         System.out.println("A");  
  19.                         state ++;  
  20.                     }  
  21.                     l.unlock();  
  22.                 }  
  23.             }  
  24.         });  
  25.         Thread B = new Thread(new Runnable(){  
  26.             @Override  
  27.             public void run() {  
  28.                 while (state<=30) {  
  29.                     l.lock();  
  30.                     if(state%3==1){  
  31.                         System.out.println("B");  
  32.                         state ++;  
  33.                     }  
  34.                     l.unlock();  
  35.                 }  
  36.             }  
  37.         });  
  38.         Thread C = new Thread(new Runnable(){  
  39.             @Override  
  40.             public void run() {  
  41.                 while (state<=30) {  
  42.                     l.lock();  
  43.                     if(state%3==2){  
  44.                         System.out.println("C");  
  45.                         state ++;  
  46.                     }  
  47.                     l.unlock();  
  48.                 }  
  49.             }  
  50.         });  
  51.         A.start();  
  52.         B.start();  
  53.         C.start();  
  54.     }  
  55.   
  56. }  

使用lock来保证只有一个线程在输出操作, 要保证了state不会被两个线程同时修改, 思路简单

还可以使用condition, condition的效率可能会更高一些, await会释放lock锁, condition的await和signal与object的wait和notify方法作用类似

signal() 代替了 notify(),await() 代替了 wait(),signalAll() 代替 notifyAll()。
await()  是等待的意思,调它 就是  阻塞写线程。
signal 是发出信号的意思,调它 就是 唤醒读线程。
  1. import java.util.concurrent.locks.Condition;  
  2. import java.util.concurrent.locks.Lock;  
  3. import java.util.concurrent.locks.ReentrantLock;  
  4.   
  5. import javax.xml.stream.events.StartDocument;  
  6.   
  7. public class ABC2 {  
  8.     private static Lock lock = new ReentrantLock();  
  9.     private static int count = 0;  
  10.     private static Condition A = lock.newCondition();  
  11.     private static Condition B = lock.newCondition();  
  12.     private static Condition C = lock.newCondition();  
  13.   
  14.     static class ThreadA extends Thread {  
  15.   
  16.         @Override  
  17.         public void run() {  
  18.             lock.lock();  
  19.             try {  
  20.                 for (int i = 0; i < 10; i++) {  
  21.                     while (count % 3 != 0)  
  22.                         A.await(); //如果不满足while条件,将本线程挂起  
  23.                     System.out.print("A");  
  24.                     count++;  
  25.                     B.signal(); // A线程执行后,唤醒下一个线程B  
  26.                 }  
  27.             } catch (InterruptedException e) {  
  28.                 e.printStackTrace();  
  29.             } finally {  
  30.                 lock.unlock();  
  31.             }  
  32.         }  
  33.   
  34.     }  
  35.   
  36.     static class ThreadB extends Thread {  
  37.   
  38.         @Override  
  39.         public void run() {  
  40.             lock.lock();  
  41.             try {  
  42.                 for (int i = 0; i < 10; i++) {  
  43.                     while (count % 3 != 1)  
  44.                         B.await();//如果不满足while条件, 将本线程挂起  
  45.                     System.out.print("B");  
  46.                     count++;  
  47.                     C.signal();// B线程执行后,唤醒下一个线程C  
  48.                 }  
  49.             } catch (InterruptedException e) {  
  50.                 e.printStackTrace();  
  51.             } finally {  
  52.                 lock.unlock();  
  53.             }  
  54.         }  
  55.   
  56.     }  
  57.   
  58.     static class ThreadC extends Thread {  
  59.   
  60.         @Override  
  61.         public void run() {  
  62.             lock.lock();  
  63.             try {  
  64.                 for (int i = 0; i < 10; i++) {  
  65.                     while (count % 3 != 2)  
  66.                         C.await();//如果不满足while条件, 将本线程挂起  
  67.                     System.out.println("C");  
  68.                     count++;  
  69.                     A.signal();// C线程执行后,唤醒下一个线程A  
  70.                 }  
  71.             } catch (InterruptedException e) {  
  72.                 e.printStackTrace();  
  73.             } finally {  
  74.                 lock.unlock();  
  75.             }  
  76.         }  
  77.   
  78.     }  
  79.   
  80.     public static void main(String[] args) throws InterruptedException {  
  81.         ThreadA threadA =new ThreadA();  
  82.         ThreadB threadB=new ThreadB();  
  83.         ThreadC threadC = new ThreadC();  
  84.         threadA.start();  
  85.         threadB.start();  
  86.         threadC.start();  
  87.         threadC.join();//让C线程执行完后在输出cout值否则可能cout在ABC线程都未完成时就输出结果。  
  88.         System.out.println(count);  
  89.     }  
  90. }  

使用信号量也可以, 这个思路最简单, 整个代码也比较简洁

1 import java.util.concurrent.Semaphore;
 2 
 3 public class ABC3 {
 4     private static Semaphore A = new Semaphore(1);
 5     private static Semaphore B = new Semaphore(1);
 6     private static Semaphore C = new Semaphore(1);
 7     
 8     static class ThreadA extends Thread {
 9 
10         @Override
11         public void run() {
12             try {
13                 for (int i = 0; i < 10; i++) {
14                     A.acquire();
15                     System.out.print("A");
16                     B.release();
17                 }
18             } catch (InterruptedException e) {
19                 e.printStackTrace();
20             }
21         }
22         
23     }
24     
25     static class ThreadB extends Thread {
26 
27         @Override
28         public void run() {
29             try {
30                 for (int i = 0; i < 10; i++) {
31                     B.acquire();
32                     System.out.print("B");
33                     C.release();
34                 }
35             } catch (InterruptedException e) {
36                 e.printStackTrace();
37             }
38         }
39         
40     }
41     
42     static class ThreadC extends Thread {
43 
44         @Override
45         public void run() {
46             try {
47                 for (int i = 0; i < 10; i++) {
48                     C.acquire();
49                     System.out.println("C");
50                     A.release();
51                 }
52             } catch (InterruptedException e) {
53                 e.printStackTrace();
54             }
55         }
56         
57     }
58     
59     public static void main(String[] args) throws InterruptedException {
60         B.acquire(); C.acquire(); // 开始只有A可以获取, BC都不可以获取, 保证了A最先执行
61         new ThreadA().start();
62         new ThreadB().start();
63         new ThreadC().start();
64     }
65 }

注意:

lock是需要lock所有者去释放的, 即谁lock, 谁释放, 不可以跨线程, 会报java.lang.IllegalMonitorStateException;

semaphore是没有所有者的说法, 可以跨线程释放和获取.

 

这是一道java笔试题, 多线程的问题现在越来越多的出现在笔试中, 要好好学习.

参考文献:

  1. 深入浅出Java Concurrency:http://www.blogjava.net/xylz/archive/2010/07/08/325587.html

Lock的await/singal 和 Object的wait/notify 的区别

在使用Lock之前,我们都使用Object 的wait和notify实现同步的。举例来说,一个producer和consumer,consumer发现没有东西了,等待,produer生成东西了,唤醒。

线程consumer线程producer
synchronize(obj){ 
    obj.wait();//没东西了,等待
}
synchronize(obj){ 
    obj.notify();//有东西了,唤醒 
}

有了lock后,世道变了,现在是:

lock.lock(); 
condition.await(); 
lock.unlock();
lock.lock(); 
condition.signal(); 
lock.unlock();

为了突出区别,省略了若干细节。区别有三点:

  1. 1. lock不再用synchronize把同步代码包装起来;
  2. 2. 阻塞需要另外一个对象condition;
  3. 3. 同步和唤醒的对象是condition而不是lock,对应的方法是await和signal,而不是wait和notify。

为什么需要使用condition呢?简单一句话,lock更灵活。以前的方式只能有一个等待队列,在实际应用时可能需要多个,比如读和写。为了这个灵活性,lock将同步互斥控制和等待队列分离开来,互斥保证在某个时刻只有一个线程访问临界区(lock自己完成),等待队列负责保存被阻塞的线程(condition完成)。

通过查看ReentrantLock的源代码发现,condition其实是等待队列的一个管理者,condition确保阻塞的对象按顺序被唤醒。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值