1、问题
使用四个线程协调工作,线程1只打印“A”,线程2只打印“B”,线程3只打印“C”,线程4只打印“D”,当线程1工作时,其他线程阻塞,当线程2工作时,其他线程阻塞......使得打印结果为“ABCDABCDABCD....”,通过输入一个数,来控制重复的次数。
2、解决方式
(1)通过synchronized修饰同步代码块,同步监视器是synchronized后括号的对象。该对象用于同步的方法如下:
wait():调用该方法的线程前提是必须获得锁,该方法被调用后就释放锁,进入等待状态,直到该线程的同步监视器notify()和 notifyAll()方法来唤醒重新获得锁,继续执行。
notify(): 唤醒在同步监视器上等待的单个线程,如果有许多线程,就随机的唤醒一个。
notifyAll(): 唤醒在此同步监视器上所有等待的线程。被唤醒的线程将无法继续进行,直到当前线程放弃此对象上的锁定。
(2)通过Lock对象保证同步,Condition对象充当同步监视器的功能。Condition对象的方法有
await(): 与隐式同步监视器的wait()类似,调用该方法,线程进入等待状态。
signal(): 与notify()类似。
signalAll(): 与notifyAll()类似。
为每个线程设置一个boolean类型的控制变量,用于控制该线程是否阻塞,每次执行完一个线程后重新设置控制变。
量的值,使得它下面一个线程可以继续执行。
3、代码。
实现方式1:使用隐式同步监视器
import java.util.Scanner; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Main { static int N = 0; // 定义4个控制变量 static boolean flagA = true; static boolean flagB = false; static boolean flagC = false; static boolean flagD = false; public static void main(String[] args) { Scanner scanner = new Scanner(System.in); N = scanner.nextInt(); scanner.close(); Main m = new Main(); // 创建一个有4个线程的线程池 ExecutorService pool = Executors.newFixedThreadPool(4); pool.submit(m.new ThreadA(m)); pool.submit(m.new ThreadB(m)); pool.submit(m.new ThreadC(m)); pool.submit(m.new ThreadD(m)); pool.shutdown(); } class ThreadA implements Runnable{ private Main m; public ThreadA(Main m){ this.m = m; } @Override public void run() { for(int i=0;i<N;i++){ // 由于wait方法进入等待时,会释放得到的锁,即只有当各个控制变量为true时,才不会阻塞 synchronized(m){ while(!flagA){ try { m.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }; } try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.print("A"); flagA = false; flagB = true; flagC = false; flagD = false; m.notifyAll(); } } } } class ThreadB implements Runnable{ private Main m; public ThreadB(Main m){ this.m = m; } @Override public void run() { for(int i=0;i<N;i++){ synchronized(m){ while(!flagB){ try { m.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }; } try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.print("B"); flagA = false; flagB = false; flagC = true; flagD = false; m.notifyAll(); } } } } class ThreadC implements Runnable{ private Main m; public ThreadC(Main m){ this.m = m; } @Override public void run() { for(int i=0;i<N;i++){ synchronized(m){ while(!flagC){ try { m.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }; } try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.print("C"); flagA = false; flagB = false; flagC = false; flagD = true; m.notifyAll(); } } } } class ThreadD implements Runnable{ private Main m; public ThreadD(Main m){ this.m = m; } @Override public void run() { for(int i=0;i<N;i++){ synchronized(m){ while(!flagD){ try { m.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }; } try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.print("D"); flagA = true; flagB = false; flagC = false; flagD = false; m.notifyAll(); } } } } }
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class MainLock { private final Lock lock = new ReentrantLock(); private final Condition cond = lock.newCondition(); static int N = 0; boolean flagA = true; boolean flagB = false; boolean flagC = false; boolean flagD = false; public static void main(String[] args) { N = 10; ExecutorService pool = Executors.newFixedThreadPool(4); MainLock main = new MainLock(); pool.submit(main.new ThreadA()); pool.submit(main.new ThreadB()); pool.submit(main.new ThreadC()); pool.submit(main.new ThreadD()); pool.shutdown(); } class ThreadA implements Runnable{ @Override public void run() { for(int i=0;i<N;i++){ lock.lock(); try { while(!flagA){ cond.await(); } System.out.print("A"); flagA = false; flagB = true; flagC = false; flagD = false; Thread.sleep(200); cond.signalAll(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ lock.unlock(); } } } } class ThreadB implements Runnable{ @Override public void run() { for(int i=0;i<N;i++){ lock.lock(); try { while(!flagB){ cond.await(); } System.out.print("B"); flagA = false; flagB = false; flagC = true; flagD = false; Thread.sleep(200); cond.signalAll(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ lock.unlock(); } } } } class ThreadC implements Runnable{ @Override public void run() { for(int i=0;i<N;i++){ lock.lock(); try { while(!flagC){ cond.await(); } System.out.print("C"); flagA = false; flagB = false; flagC = false; flagD = true; Thread.sleep(200); cond.signalAll(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ lock.unlock(); } } } } class ThreadD implements Runnable{ @Override public void run() { for(int i=0;i<N;i++){ lock.lock(); try { while(!flagD){ cond.await(); } System.out.print("D"); flagA = true; flagB = false; flagC = false; flagD = false; Thread.sleep(200); cond.signalAll(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ lock.unlock(); } } } } }