线程之示例
一.多种方式解决一个生产者与消费者问题
二.交替打印问题
1.A、B两个线程交替打印1 -- 100
2.Java实现两个线程交替打印问题
3.java多线程交替打印A和B?
4.java多线程 更优雅的实现线程同步:交替打印A、B LockSupport实现
一. 如何解决一个生产者与消费者问题
多线程同步经典问题。生产者和消费者同时使用一块缓冲区,生产者生产商品放入缓冲区,消费者从缓冲区中取出商品。需要保证是,当缓冲区满时,生产者不可生产商品;当缓冲区为空时,消费者不可取出商品。
几种解决同步问题的方式
(1)wait()与notify()
(2)Lock与Condition机制
(3)BlockingQueue阻塞队列
【1】object类的wait()与notify()方法
wait()用在以下场合:
(1)当缓冲区满时,缓冲区调用wait()方法,使得生产者释放锁,当前线程阻塞,其他线程可以获得锁。
(2)当缓冲区空时,缓冲区调用wait()方法,使得消费者释放锁,当前线程阻塞,其他线程可以获得锁。
notify()用在以下场合:
(1)当缓冲区未满时,生产者生产商品放入缓冲区,然后缓冲区调用notify()方法,通知上一个因wait()方法释放锁的线程现在可以去获得锁了,同步块代码执行完成后,释放对象锁,此处的对象锁,锁住的是缓冲区。
(2)当缓冲区不为空时,消费者从缓冲区中取出商品,然后缓冲区调用notify()方法,通知上一个因wait()方法释放锁的线程现在可以去获得锁了,同步块代码执行完成后,释放对象锁。
// 生产者消费者问题 public class ProAndCon { public static final int MAX_SIZE = 2; public static LinkedList<Integer> list = new LinkedList<>(); class Producer implements Runnable { public void run() { synchronized (list) { //仓库容量已经达到最大值 while (list.size() == MAX_SIZE) { System.out.println("仓库已满,生产者" + currentThread().getName() + "不可生产."); try { list.wait(); } } list.add(1); System.out.println("生产者" + currentThread().getName() + "生产, 仓库容量为" + list.size()); list.notify(); }}} class Consumer implements Runnable { public void run() { synchronized (list) { while (list.size() == 0) { System.out.println("仓库为空,消费者" + currentThread().getName() + "不可消费"); try { list.wait(); } } list.removeFirst(); System.out.println("消费者" + currentThread().getName() + "消费,仓库容量为" + list.size()); list.notify(); }} } public static void main(String[] args) { ProAndCon proAndCon = new ProAndCon(); Producer producer = proAndCon.new Producer(); Consumer consumer = proAndCon.new Consumer(); for (int i = 0; i < 10; i++) { Thread pro = new Thread(producer); pro.start(); Thread con = new Thread(consumer); con.start(); }}}
【2】Lock与Condition机制
JDK5.0之后,Java提供Lock与Condition机制。Condition接口的await()和signal()是做同步的两种方法,它们功能基本上和Object的wait()、nofity()相同,或者说可以取代它们,但是它们和Lock机制是直接挂钩的。
通过在Lock对象上调用newCondition()方法,将条件变量和一个锁对象进行绑定,进而控制并发程序访问竞争资源的安全。
public class ProAndCon2 { public static final int MAX_SIZE = 2; public static LinkedList<Integer> list = new LinkedList<>(); public static Lock lock = new ReentrantLock(); //仓库满的条件变量 public static Condition full = lock.newCondition(); //仓库空的条件变量 public static Condition empty = lock.newCondition(); class Producer implements Runnable { public void run() { lock.lock(); while (list.size() == MAX_SIZE) { try { System.out.println("仓库已满,生产者" +currentThread().getName() + "不可生产."); full.await(); } } list.add(1); System.out.println("生产者" + currentThread().getName() + "生产, 仓库容量为" + list.size()); //唤醒其他生产者与消费者线程 full.signal(); empty.signal(); lock.unlock();} } class Consumer implements Runnable { public void run() { lock.lock(); while (list.size() == 0) { try { System.out.println("仓库为空,消费者" +currentThread().getName() + "不可消费."); empty.await(); } } list.removeFirst(); System.out.println("消费者" +currentThread().getName() + "消费,仓库容量为" + list.size()); //唤醒其他生产者与消费者线程 full.signal(); empty.signal(); lock.unlock(); }} public static void main(String[] args) { ProAndCon2 proAndCon = new ProAndCon2(); Producer producer = proAndCon.new Producer(); Consumer consumer = proAndCon.new Consumer(); for (int i = 0; i < 10; i++) { Thread pro = new Thread(producer); pro.start(); Thread con = new Thread(consumer); con.start(); } }}
【3】使用BlockingQueue阻塞队列
什么是阻塞队列?
如果向一个已经满了的队列中添加元素或者从空队列中移除元素,都将会导致线程阻塞,线程一直等待到有旧元素被移除或新元素被添加的时候,才能继续执行。
JDK 1.5 以后新增BlockingQueue接口,我用它实现类,ArrayBlockingQueue或者是LinkedBlockingQueue。
怎么使用LinkedBlockingQueue?
用LinkedBlockingQueue来解决生产者与消费者问题,主要用到它put()与take()
put():向阻塞队列中添加一个元素,队列满时,自动阻塞。
take():从阻塞队列中取出一个元素,队列空时,自动阻塞。
原理:其实LinkedBlockingQueue底层使用的仍然是Lock与Condition机制,从源码知道
//..............用到了Lock与Condition机制。 LinkedBlockingQueue底层已经解决好了同步问题,可方便使用它。
// 解决生产者与消费者问题: 采用阻塞队列BlockingQueue public class ProAndCon3 { public static final int MAX_SIZE = 2; public static BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(MAX_SIZE); class Producer implements Runnable { public void run() { if (queue.size() == MAX_SIZE) { System.out.println("仓库已满,生产者" + currentThread().getName() + "不可生产."); } try { queue.put(1); System.out.println("生产者" +currentThread().getName() + "生产, 仓库容量为" + queue.size()); } }} class Consumer implements Runnable { public void run() { if (queue.size() == 0) { System.out.println("仓库为空,消费者" + currentThread().getName() + "不可消费."); } try { queue.take(); System.out.println("消费者" + currentThread().getName() + "消费,仓库容量为" + queue.size()); } }} public static void main(String[] args) { ProAndCon3 proAndCon = new ProAndCon3(); Producer producer = proAndCon.new Producer(); Consumer consumer = proAndCon.new Consumer(); for (int i = 0; i < 10; i++) { Thread pro = new Thread(producer); pro.start(); Thread con = new Thread(consumer); con.start(); }}}
二. 多线程交替打印问题
2.1. 多线程交替打印A和B?
关键就是打印A和B的方法,synchronized的对象是同一个,即持有的锁是同一个,都是print这个类的实例
然后又一个标志,当不是自己的时候,就释放自己的锁,并让线程进入等待状态,就是this.wait()
当while执行完后,说明到自己的回合,打印字符,设置标志,然后唤醒锁为this的线程。
class Print{ boolean nowA=true; synchronized void printA(){ while(!nowA){ try { this.wait(); } } nowA=false; System.out.println(Thread.currentThread()+""+"A"); this.notify(); } synchronized void printB(){ while(nowA){ try { this.wait(); } } nowA=true; System.out.println(Thread.currentThread()+" "+"B"); this.notify(); }} public class Test3 { public static void main(String[] args){ Print print=new Print(); new Thread(new Runnable() { @Override public void run() { for(int i=0;i<10;i++){ print.printA(); } } }).start(); new Thread(new Runnable() { @Override public void run() { for(int i=0;i<10;i++){ print.printB(); } } }).start();; }}
2.2 .java多线程 更优雅的实现线程同步:交替打印A、B LockSupport实现
一 问题概述
线程或者进程之间有两种关系:同步和互斥,通常实现同步方法是使用线程的等待唤醒机制,而等待唤醒机制使用是建立在互斥继承上的。
但是同步线程并不一定是必须要实现互斥的。比如一个线程打印A,一个线程打印B。这两个线程就没有互斥关系,但是提出这么个需求:交替打印A、B ,
一般往往要使用wait()/notify机制。
二 LockSupport 介绍
LockSupport作为一个工具类,主要学习它的方法。
park():在线程内调用,表示当前线程自我阻塞,直到获得许可证
park(线程变量):让指定的线程获得许可证。
一看这两个方法的定义,显然可以利用这两个方法实现线程的顺序调用(同步)
三 两种思路实现交替打印A/B
* 交替打印A/B 等待唤醒机制
public class Test3 { static class MyRun implements Runnable { static int i = 0; @Override public synchronized void run() { for (int j = 0; j < 10; j++) { if(i%2==0) System.out.println(Thread.currentThread().getName()+":A"); else System.out.println(Thread.currentThread().getName()+":B"); i++; this.notifyAll(); try { if(i>=19) Thread.sleep(10); else this.wait(); } } }} public static void main(String[] args) { MyRun myRun = new MyRun(); Thread a = new Thread(myRun); Thread b = new Thread(myRun); a.start(); b.start(); }} //LockSupport实现: 交替打印A,B LockSupport实现 public class Test2 { static Thread a=null; static Thread b=null; public static void main(String[] args) { a= new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { LockSupport.park(); System.out.println(currentThread().getName()+":B"); LockSupport.unpark(b); } }}); b=new Thread((new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(currentThread().getName()+":A"); LockSupport.unpark(a); LockSupport.park(); } }})); a.start(); b.start(); }}
2.3 Java实现两个线程交替打印问题
线程1负责打印a,b,c,d
线程2负责打印1,2,3,4;要求控制台中输出的内容为 a1b2c3d4
public class TestMain { static final Object object = new Object(); public static void main(String[] args) throws InterruptedException { new Thread(new Runnable() { String a[] = {"a","b","c","d"}; @Override public void run() { for(int i=0;i< 4 ;i++){ synchronized (object){ System.out.println("线程a 开始执行"); object.notify(); try { System.out.println("线程a 开始等待"); object.wait(); } System.out.println("线程a 继续执行"); System.out.println(a[i]); System.out.println("线程a 执行结束"); object.notify(); }} }}).start(); new Thread(new Runnable() { int a[] = {1,2,3,4}; @Override public void run() { for(int i=0;i<4;i++){ synchronized (object){ System.out.println("线程1 开始执行"); object.notify(); try { System.out.println("线程1 开始等待"); object.wait(); } System.out.println("线程1 继续执行"); System.out.println(a[i]); System.out.println("线程1 执行结束"); } } }}).start(); }}
2.4 .A、B两个线程交替打印1 -- 100
方案一: public class Test100_02 { private static Object object = new Object(); private static boolean isFlag = false; public static void main(String[] args) { Thread t1 = new Thread(() -> { for (int i = 1; i <= 999; i+=2) { synchronized (object) { if (!isFlag) { System.out.println("current:" + i); isFlag = true; try { object.wait(); } object.notify(); } } }}); Thread t2 = new Thread(() -> { for (int i = 2; i <= 1000; i+=2) { synchronized (object) { if (isFlag) { isFlag = false; System.out.println("current:" + i); object.notify(); try { object.wait(); } } }} }); t1.setPriority(Thread.MAX_PRIORITY); t1.start(); t2.start(); }}
方案二: public class Test100_01 { private static Lock lock = new ReentrantLock(); private static Condition condition1 = lock.newCondition(); private static Condition condition2 = lock.newCondition(); public static void main(String[] args) { Thread t1 = new Thread(() -> { for (int i = 1; i <= 999; i += 2) { lock.lock(); try { System.out.println("current:" + i); condition1.await(); condition2.signal(); } catch (InterruptedException e) { e.printStackTrace(); } lock.unlock(); }}); Thread t2 = new Thread(() -> { for (int i = 2; i <= 1000; i += 2) { lock.lock(); try { System.out.println("current:" + i); condition1.signal(); condition2.await(); } lock.unlock(); } }); t1.setPriority(Thread.MAX_PRIORITY); t1.start(); t2.start(); }} 你的代码我运行了貌似会在中途卡住, 我改进了一下顺序 condition2.signal(); System.out.println("current:" + i); condition1.await(); condition1.signal(); System.out.println("current:" + i); condition2.await();
41.生产者消费者模式 class Clerk {//店员类 private int product = 0; public synchronized void get() {// 进货 while (product >= 1) { System.out.println("产品已满!"); try { this.wait();} } System.out.println(currentThread().getName() + ":" + ++product); this.notifyAll(); // 唤醒 } public synchronized void sale() {// 售货 while (product <= 0) { System.out.println("缺货!"); try { this.wait();// 等待} } System.out.println(currentThread().getName() + ":" + --product); this.notifyAll();// 唤醒} } class Productor implements Runnable {// 生产者类 private Clerk clerk; public Productor(Clerk clerk) { this.clerk = clerk; } @Override public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(100); } clerk.get();} } } class Consumer implements Runnable {// 消费者类 private Clerk clerk; public Consumer(Clerk clerk) { this.clerk = clerk; } @Override public void run() { for (int i = 0; i < 10; i++) { clerk.sale(); }} } public class TestProductorAndConsumer { public static void main(String[] args) { Clerk clerk = new Clerk(); Productor productor = new Productor(clerk); Consumer consumer = new Consumer(clerk); new Thread(productor, "Productor A").start(); new Thread(consumer, "Consumer B").start(); new Thread(productor, "Productor C").start(); new Thread(consumer, "Consumer D").start(); } } 42.线程交替打印 //其他方法: https://segmentfault.com/a/1190000021433079?utm_source=sf-similar-article //**** 1) 3个线程交替打印0->100 public class Main012 { public static void main(String[] args) { int threadCount = 3; int max = 100; for (int i = 0; i < threadCount; i++) { new Thread(new PrintSequenceThread(i, threadCount, max)).start();} } } class PrintSequenceThread implements Runnable { private static final Object mLock = new Object(); private static int current = 0;//当前即将打印的数字 private int threadNo;//前线程编号,从0开始 private int threadCount; private int max;//打印的最大值 public PrintSequenceThread(int threadNo, int threadCount, int max) { this.threadNo = threadNo; this.threadCount = threadCount; this.max = max; } public void run() { while (true) { synchronized (mLock) { // 判断是否轮到当前线程执行 while (current % threadCount != threadNo) { if (current > max) {break;} try { mLock.wait();// 如果不是,则当前线程进入wait } } // 最大值跳出循环 if (current > max) {break;} System.out.println("thread-" + threadNo + " : " + current); current++; mLock.notifyAll(); //唤醒其他wait线程 } }} } //************ //2) 3个线程并发打印从0到n,不是交替打印,每个线程可能连续执行,有的线程可能长时间按得不到执行 public class Main12_1 { public static void main(String[] args) { int threadCount = 3; for (int i = 0; i < threadCount; i++) { new Thread(new PrintSequenceThread1(i, threadCount)).start(); } } } class PrintSequenceThread1 implements Runnable { private static final Object mLock = new Object(); private static int current = 0; // 当前即将打印的数字 private int threadNo; // 当前线程编号,从0开始 private int threadCount; public PrintSequenceThread1(int threadNo, int threadCount) { this.threadNo = threadNo; this.threadCount = threadCount; } @Override public void run() {//3个线程并发打印从0到n,不是交替打印,每个线程可能连续执行,有的线程可能长时间按得不到执行 while (true) { synchronized (mLock) { try { Thread.sleep(100); } System.out.println("thread-" + threadNo + " : " + current); current++; }} } } //**** 3) 3个线程交替打印从0到n public class Main12_2 { public static void main(String[] args) { int threadCount = 3; for (int i = 0; i < threadCount; i++) { //3个线程交替打印从0到n new Thread(new PrintSequenceThread2(i, threadCount)).start(); }} } class PrintSequenceThread2 implements Runnable { private static final Object mLock = new Object(); private static int current = 0;//当前即将打印的数字 private int threadNo;// 当前线程编号,从0开始 private int threadCount;//线程数量 public PrintSequenceThread2(int threadNo, int threadCount) { this.threadNo = threadNo; this.threadCount = threadCount; } public void run() {//3个线程交替打印从0到n while (true) { synchronized (mLock) { // 判断是否轮到当前线程执行 while (current % threadCount != threadNo) {//一定是while,而不是if try { //是while的原因,当这个线程被notifyAll时,它会从mLock.wait()这句之后继续往下执行,即使当前没有轮到它 //写成while,如果它依然满足条件(没有轮到它),它会一直阻塞在这里 // 如果不是,则当前线程进入wait mLock.wait(); } } try { Thread.sleep(100);//停顿100ms,为了让打印不要太快 } System.out.println("thread-" + threadNo + " : " + current); current++; mLock.notifyAll();} } } }
二. 三个线程交替打印ABC
如果要实现3个线程交替打印ABC呢?这次打算使用重入锁,和上面没差多少,但是由于现在有三个线程了,在打印完后需要唤醒其他线程,注意不可使用sigal()
,因为唤醒的线程是随机的,不能保证打印顺序不说,还会造成死循环。一定要使用sigalAll()
唤醒所有线程。
public class ThreeThreadPrintABC { private static ReentrantLock lock = new ReentrantLock(); private static Condition wait = lock.newCondition(); // 用来控制该打印的线程 private static int count = 0; static class PrintA implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { lock.lock(); try { while ((count % 3) != 0) { wait.await(); } System.out.println(Thread.currentThread().getName() + " A"); count++; wait.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } } static class PrintB implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { lock.lock(); try { while ((count % 3) != 1) { wait.await(); } System.out.println(Thread.currentThread().getName() + " B"); count++; wait.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } } static class PrintC implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { lock.lock(); try { while ((count % 3) != 2) { wait.await(); } System.out.println(Thread.currentThread().getName() + " C"); count++; wait.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } } public static void main(String[] args) { Thread printA = new Thread(new PrintA()); Thread printB = new Thread(new PrintB()); Thread printC = new Thread(new PrintC()); printA.start(); printB.start(); printC.start(); } } 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 Thread-2 C
如果觉得不好理解,重入锁是可以绑定多个条件的。创建3个Condition分别让三个打印线程在上面等待。A打印完了,唤醒等待在conditionB对象上的PrintB;B打印完了唤醒在conditionC对象上的PrintC;C打印完了,唤醒在conditionA对象上等待的PrintA,如此循环地唤醒对方即可。
public class ThreeThreadPrintABC1 { private static ReentrantLock lock = new ReentrantLock(); private static Condition conditionA = lock.newCondition(); private static Condition conditionB = lock.newCondition(); private static Condition conditionC = lock.newCondition(); // 用来控制该打印的线程 private static int count = 0; static class PrintA implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { lock.lock(); try { if (count % 3 != 0) { conditionA.await(); } System.out.println(Thread.currentThread().getName() + "------A"); count++; conditionB.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } } static class PrintB implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { lock.lock(); try { if (count % 3 != 1) { conditionB.await(); } System.out.println(Thread.currentThread().getName() + "------B"); count++; conditionC.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } } static class PrintC implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { lock.lock(); try { if (count % 3 != 2) { conditionC.await(); } System.out.println(Thread.currentThread().getName() + "------C"); count++; conditionA.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } } public static void main(String[] args) { Thread printA = new Thread(new PrintA()); Thread printB = new Thread(new PrintB()); Thread printC = new Thread(new PrintC()); printA.start(); printB.start(); printC.start(); } } 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 Thread-2------C
另一种实现:
/** * 基于一个ReentrantLock和三个conditon实现连续打印abcabc... */ public class PrintABCTest implements Runnable { // 打印次数 private static final int PRINT_COUNT = 10; // 打印锁 private final ReentrantLock lock; // 本线程打印所需的condition private final Condition thisCondition; // 下一个线程打印所需的condition private final Condition nextCondition; // 打印字符 private final char printChar; public PrintABCTest(ReentrantLock lock, Condition thisCondition, Condition nextCondition, char printChar) { this.lock = lock; this.thisCondition = thisCondition; this.nextCondition = nextCondition; this.printChar = printChar; } @Override public void run() { // 获取打印锁 进入临界区 lock.lock(); try { // 连续打印PRINT_COUNT次 for (int i = 0; i < PRINT_COUNT; i++) { //打印字符 System.out.println(printChar); // 使用nextCondition唤醒下一个线程 // 因为只有一个线程在等待,所以signal或者signalAll都可以 nextCondition.signal(); // 不是最后一次则通过thisCondtion等待被唤醒 // 必须要加判断,不然虽然能够打印10次,但10次后就会直接死锁 if (i < PRINT_COUNT -1) { try { // 本线程让出锁并等待唤醒 thisCondition.await(); } catch (InterruptedException e) { e.printStackTrace(); } } } } finally { // 释放打印锁 lock.unlock(); } } public static void main(String[] args) throws InterruptedException { // 写锁 ReentrantLock lock = new ReentrantLock(); // 打印a线程的condition Condition conditionA = lock.newCondition(); // 打印b线程的condition Condition conditionB = lock.newCondition(); // 打印c线程的condition Condition conditionC = lock.newCondition(); // 实例化A线程 Thread printerA = new Thread(new PrintABCTest(lock, conditionA, conditionB, 'A')); // 实例化B线程 Thread printerB = new Thread(new PrintABCTest(lock, conditionB, conditionC, 'B')); // 实例化C线程 Thread printerC = new Thread(new PrintABCTest(lock, conditionC, conditionA, 'C')); // 依次开始A B C线程 printerA.start(); Thread.sleep(100); printerB.start(); Thread.sleep(100); printerC.start(); } } A B C A B C A B C A B C
一.两个线程交替打印奇偶数
方法1:使用同步方法实现
public class Num { private int count = 1; public synchronized void printOdd() {//打印奇数 try { if (count % 2 != 1) { this.wait(); } System.out.println(Thread.currentThread().getName() + "----------" + count); count++; this.notify(); } catch (InterruptedException e) { e.printStackTrace(); } } public synchronized void printEven() {//打印偶数 try { if (count % 2 != 0) { this.wait(); } System.out.println(Thread.currentThread().getName() + "----------" + count); count++; this.notify(); } catch (InterruptedException e) { } } } /** * 打印奇数的线程 */ public class Odd implements Runnable{ private Num num; public Odd(Num num) { this.num = num; } @Override public void run() { for (int i = 0; i < 5; i++) { num.printOdd(); } } } /** * 打印偶数的线程 */ public class Even implements Runnable { private Num num; public Even(Num num) { this.num = num; } @Override public void run() { for (int i = 0; i < 5; i++) { num.printEven(); } } } public class TestPrint { public static void main(String[] args) { Num num = new Num(); Odd odd = new Odd(num); Even even = new Even(num); Thread t1 = new Thread(odd, "threadOdd"); Thread t2 = new Thread(even, "threadEven"); t1.start(); t2.start(); } } threadOdd----------1 threadEven----------2 threadOdd----------3 threadEven----------4 threadOdd----------5 threadEven----------6 threadOdd----------7 threadEven----------8 threadOdd----------9 threadEven----------10
方法2:使用同步块实现
public class Number { private int count = 1; private Object lock = new Object(); public void printOdd() {//打印奇数 synchronized(lock) { try { if(count % 2 != 1) { lock.wait(); } System.out.println(Thread.currentThread().getName() + "=======" + count); count++; lock.notify(); } catch(InterruptedException e) { } } } public void printEven() {//打印偶数 synchronized(lock) { try { if(count % 2 != 0) { lock.wait(); } System.out.println(Thread.currentThread().getName() + "=======" + count); count++; lock.notify(); } catch(InterruptedException e) { } } } } /** * 打印奇数的线程 */ public class OddPrinter implements Runnable { private Number num; public OddPrinter(Number num) { this.num = num; } public void run() { for (int i = 0; i < 5; i++) { num.printOdd(); } } } /** * 打印偶数的线程 */ public class EvenPrinter implements Runnable{ private Number num; public EvenPrinter(Number num) { this.num = num; } public void run() { for (int i = 0; i < 5; i++) { num.printEven(); } } } public class PrintOddEven { public static void main(String[] args) { Number num = new Number(); OddPrinter oddPrinter = new OddPrinter(num); EvenPrinter evenPrinter = new EvenPrinter(num); Thread oddThread = new Thread(oddPrinter, "oddThread"); Thread evenThread = new Thread(evenPrinter, "evenThread"); oddThread.start(); evenThread.start(); } } oddThread=======1 evenThread=======2 oddThread=======3 evenThread=======4 oddThread=======5 evenThread=======6 oddThread=======7 evenThread=======8 oddThread=======9 evenThread=======10
方法3:使用ReentrantLock实现
public class Number1 { private int count = 1; private ReentrantLock lock = new ReentrantLock(); // 为打印奇数的线程注册一个Condition public Condition conditionOdd = lock.newCondition(); // 为打印偶数的线程注册一个Condition public Condition conditionEven = lock.newCondition(); public void printOdd() {//打印奇数 try { lock.lock(); if(count % 2 != 1) { conditionOdd.await(); } System.out.println(Thread.currentThread().getName() + "=======" + count); count++; conditionEven.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printEven() {//打印偶数 try { lock.lock(); if(count % 2 != 0) { conditionEven.await(); } System.out.println(Thread.currentThread().getName() + "=======" + count); count++; conditionOdd.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } public class PrintOddEven1 { public static void main(String[] args) { final Number1 num = new Number1(); /** * 打印奇数的线程 */ Thread oddThread = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 5; i++) { num.printOdd(); } } }, "oddThread"); /** * 打印偶数的线程 */ Thread evenThread = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 5; i++) { num.printEven(); } } }, "evenThread"); oddThread.start(); evenThread.start(); } } oddThread=======1 evenThread=======2 oddThread=======3 evenThread=======4 oddThread=======5 evenThread=======6 oddThread=======7 evenThread=======8 oddThread=======9 evenThread=======10