线程之示例

一.多种方式解决一个生产者与消费者问题
二.交替打印问题
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();
}}
方案一:
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

 

posted on 2020-04-01 00:23  左手指月  阅读(575)  评论(0编辑  收藏  举报