线程:用三个线程控制循环顺序输出10次ABC"/>
Java多线程:用三个线程控制循环顺序输出10次ABC
题目:有A,B,C三个线程, A线程输出A, B线程输出B, C线程输出C,要求, 同时启动三个线程, 按顺序输出ABC, 循环10次。
解题思路:要按顺序输出ABC, 循环10次,就要控制三个线程同步工作,也就是说要让三个线程轮流输出,直到10个ABC全部输出则结束线程。这里用一个Lock对象来控制三个线程的同步。用一个int型变量state标识由那个线程输出。
- package com.thread;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
- public class ABC {
- private static int state = 0;
- public static void main(String[] args) {
- final Lock l = new ReentrantLock();
- Thread A = new Thread(new Runnable(){
- @Override
- public void run() {
- while (state<=30) {
- l.lock();
- if(state%3==0){
- System.out.println("A");
- state ++;
- }
- l.unlock();
- }
- }
- });
- Thread B = new Thread(new Runnable(){
- @Override
- public void run() {
- while (state<=30) {
- l.lock();
- if(state%3==1){
- System.out.println("B");
- state ++;
- }
- l.unlock();
- }
- }
- });
- Thread C = new Thread(new Runnable(){
- @Override
- public void run() {
- while (state<=30) {
- l.lock();
- if(state%3==2){
- System.out.println("C");
- state ++;
- }
- l.unlock();
- }
- }
- });
- A.start();
- B.start();
- C.start();
- }
- }
- package com.thread;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
- public class ABC {
- private static int state = 0;
- public static void main(String[] args) {
- final Lock l = new ReentrantLock();
- Thread A = new Thread(new Runnable(){
- @Override
- public void run() {
- while (state<=30) {
- l.lock();
- if(state%3==0){
- System.out.println("A");
- state ++;
- }
- l.unlock();
- }
- }
- });
- Thread B = new Thread(new Runnable(){
- @Override
- public void run() {
- while (state<=30) {
- l.lock();
- if(state%3==1){
- System.out.println("B");
- state ++;
- }
- l.unlock();
- }
- }
- });
- Thread C = new Thread(new Runnable(){
- @Override
- public void run() {
- while (state<=30) {
- l.lock();
- if(state%3==2){
- System.out.println("C");
- state ++;
- }
- l.unlock();
- }
- }
- });
- A.start();
- B.start();
- C.start();
- }
- }
使用lock来保证只有一个线程在输出操作, 要保证了state不会被两个线程同时修改, 思路简单
还可以使用condition, condition的效率可能会更高一些, await会释放lock锁, condition的await和signal与object的wait和notify方法作用类似
signal() 代替了 notify(),await() 代替了 wait(),signalAll() 代替 notifyAll()。 await() 是等待的意思,调它 就是 阻塞写线程。 signal 是发出信号的意思,调它 就是 唤醒读线程。
- import java.util.concurrent.locks.Condition;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
- import javax.xml.stream.events.StartDocument;
- public class ABC2 {
- private static Lock lock = new ReentrantLock();
- private static int count = 0;
- private static Condition A = lock.newCondition();
- private static Condition B = lock.newCondition();
- private static Condition C = lock.newCondition();
- static class ThreadA extends Thread {
- @Override
- public void run() {
- lock.lock();
- try {
- for (int i = 0; i < 10; i++) {
- while (count % 3 != 0)
- A.await(); //如果不满足while条件,将本线程挂起
- System.out.print("A");
- count++;
- B.signal(); // A线程执行后,唤醒下一个线程B
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- }
- }
- }
- static class ThreadB extends Thread {
- @Override
- public void run() {
- lock.lock();
- try {
- for (int i = 0; i < 10; i++) {
- while (count % 3 != 1)
- B.await();//如果不满足while条件, 将本线程挂起
- System.out.print("B");
- count++;
- C.signal();// B线程执行后,唤醒下一个线程C
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- }
- }
- }
- static class ThreadC extends Thread {
- @Override
- public void run() {
- lock.lock();
- try {
- for (int i = 0; i < 10; i++) {
- while (count % 3 != 2)
- C.await();//如果不满足while条件, 将本线程挂起
- System.out.println("C");
- count++;
- A.signal();// C线程执行后,唤醒下一个线程A
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- }
- }
- }
- public static void main(String[] args) throws InterruptedException {
- ThreadA threadA =new ThreadA();
- ThreadB threadB=new ThreadB();
- ThreadC threadC = new ThreadC();
- threadA.start();
- threadB.start();
- threadC.start();
- threadC.join();//让C线程执行完后在输出cout值否则可能cout在ABC线程都未完成时就输出结果。
- System.out.println(count);
- }
- }
使用信号量也可以, 这个思路最简单, 整个代码也比较简洁
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笔试题, 多线程的问题现在越来越多的出现在笔试中, 要好好学习.
参考文献:
- 深入浅出Java Concurrency:.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. lock不再用synchronize把同步代码包装起来;
- 2. 阻塞需要另外一个对象condition;
- 3. 同步和唤醒的对象是condition而不是lock,对应的方法是await和signal,而不是wait和notify。
为什么需要使用condition呢?简单一句话,lock更灵活。以前的方式只能有一个等待队列,在实际应用时可能需要多个,比如读和写。为了这个灵活性,lock将同步互斥控制和等待队列分离开来,互斥保证在某个时刻只有一个线程访问临界区(lock自己完成),等待队列负责保存被阻塞的线程(condition完成)。
通过查看ReentrantLock的源代码发现,condition其实是等待队列的一个管理者,condition确保阻塞的对象按顺序被唤醒。
更多推荐
Java多线程:用三个线程控制循环顺序输出10次ABC
发布评论