两种方式实现线程通信:三个线程交替打印AABBCC

多线程之间是抢占资源的,使用线程通信可以达到线程按序执行的目的

线程共享资源类, 首先创建一个资源类, 包含三个打印的方法以及首次打印的字符串
多个线程访问,方法加synchronized同步锁

class Resource {

    String currentPrint = "AA"; // 初始打印,打印之后赋值为下一个要打印的内容

    /**
     * 打印AA
     * @param next 下一个要打印的字符串
     * @throws InterruptedException
     */
    public synchronized void printAA(String next) throws InterruptedException {
      	// 不是要打印的内容, 阻塞线程
        while (!Objects.equals(currentPrint, "AA")) {
            wait();
        }
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName() + ": AA");
        }
        currentPrint= next;
        // 唤醒其他线程
        notifyAll();
    }

    /**
     * 打印BB
     * @param next 下一个要打印的字符串
     * @throws InterruptedException
     */
    public synchronized void printBB(String next) throws InterruptedException {
      	// 不是要打印的内容, 阻塞线程
        while (!Objects.equals(currentPrint, "BB")) {
            wait();
        }
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName() + ": BB");
        }
        currentPrint= next;
        // 唤醒其他线程
        notifyAll();
    }

    /**
     * 打印CC
     * @throws InterruptedException
     */
    public synchronized void printCC() throws InterruptedException {
    	// 不是要打印的内容, 阻塞线程
        while (!Objects.equals(currentPrint, "CC")) {
            wait();
        }
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName() + ": CC");
        }
    }
}

创建线程执行程序进行交替打印

public class AABBCC {

    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = null;
        try {
            // 创建一个最大长度为3的线程池
            threadPoolExecutor = new ThreadPoolExecutor(3, 3, 0,
                    TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(10), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
            Resource resource = new Resource();
            // 将要执行的任务插入到集合中
            List<Runnable> list = new ArrayList<>();
            list.add(()->{try { resource.printAA("BB"); } catch (InterruptedException e) { e.printStackTrace(); }});
            list.add(()->{try { resource.printBB("CC"); } catch (InterruptedException e) { e.printStackTrace(); }});
            list.add(()->{try { resource.printCC();} catch (InterruptedException e) {e.printStackTrace();}});
            for (int i = 0; i < list.size(); i++) {
                threadPoolExecutor.execute(list.get(i));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭线程池
            if (threadPoolExecutor != null) {
                threadPoolExecutor.shutdown();
            }
        }
    }
}

执行结果
在这里插入图片描述
资源类冗余代码进行优化

    /**
     * 打印字符串
     * @param currentPrintParam 当前
     * @param next 下一个
     * @param times 打印次数
     * @throws InterruptedException
     */
    public synchronized void printStr(String currentPrintParam,String next, int times) throws InterruptedException {
      	// 不是要打印的内容, 阻塞线程
        while (!Objects.equals(currentPrint, currentPrintParam)) {
            wait();
        }
        for (int i = 0; i < times; i++) {
            System.out.println(Thread.currentThread().getName() + ": "+ currentPrint);
        }
        currentPrint = next;
        // 唤醒其他线程
        notifyAll();
    }

调用方法替换

            Resource resource = new Resource();
            // 将要执行的任务插入到集合中
            List<Runnable> list = new ArrayList<>();
            list.add(()->{try { resource.printStr("AA", "BB", 3); } catch (InterruptedException e) { e.printStackTrace(); }});
            list.add(()->{try { resource.printStr("BB", "CC", 3); } catch (InterruptedException e) { e.printStackTrace(); }});
            list.add(()->{try { resource.printStr("CC", "", 3);} catch (InterruptedException e) {e.printStackTrace();}});
            for (int i = 0; i < list.size(); i++) {
                threadPoolExecutor.execute(list.get(i));
            }

执行结果
在这里插入图片描述
JDK5在java.util.concurrent包下提供了Lock并发锁
资源类代码新增

    Lock lock = new ReentrantLock(); // 并发锁
    Condition aCondition = lock.newCondition();
    Condition bCondition = lock.newCondition();
    Condition cCondition = lock.newCondition();

    /**
     * 打印字符串
     * @param currentPrintParam 当前线程打印
     * @param nextPrint 下一个线程打印
     * @param currentCondition 当前线程
     * @param nextCondition 下一个线程
     * @param times 打印次数
     * @throws InterruptedException
     */
    public void lockPrintStr(String currentPrintParam, String nextPrint, Condition currentCondition, Condition nextCondition, int times) {
        lock.lock();
        try {
            // 不是要打印的内容 阻塞线程
            while (!Objects.equals(currentPrint, currentPrintParam)) {
                currentCondition.await();
            }
            for (int i = 0; i < times; i++) {
                System.out.println(Thread.currentThread().getName() + ": "+ currentPrint);
            }
            currentPrint = nextPrint;
            // 唤醒下一个打印条件的线程
            if (!Objects.isNull(nextCondition)) {
                nextCondition.signal();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 释放锁
            if (lock != null) {
                lock.unlock();
            }
        }
    }

调用新增的方法

            Resource resource = new Resource();
            // 将要执行的任务插入到集合中
            List<Runnable> list = new ArrayList<>();
            list.add(()->{resource.lockPrintStr("AA", "BB", resource.aCondition, resource.bCondition, 3);});
            list.add(()->{resource.lockPrintStr("BB", "CC", resource.bCondition, resource.cCondition, 3);});
            list.add(()->{resource.lockPrintStr("CC", "", resource.cCondition, null, 3);});
            for (int i = 0; i < list.size(); i++) {
                threadPoolExecutor.execute(list.get(i));
            }

执行结果
在这里插入图片描述
多次执行的结果中可以看出,由三个线程交替打印AABBCC

总结:

  1. 线程通信其中两种方式 Object类下wait()、notify()/notifyAll(),以及JDK5以后提供的Lock下Condition内部类的await()、signal()方法。
  2. 使用wait/notifyAll进行线程通信唤醒唤醒了所有线程,增加了上下文的切换时间,使用await/signal可以实现精准唤醒,java集合框架中的队列就采用了后者实现线程通信

猜你喜欢

转载自blog.csdn.net/qq_37132814/article/details/100928326