[Java SDK] [多线程] Java中线程的定制化通信

简介: [Java SDK] [多线程] Java中线程的定制化通信

简介

线程的定制化通信:顾名思义多线程的循环交替执行。 有一个很经典的案例如下:
三个线程之间按顺序调用,实现 A -> B -> C 三个线程启动,要求如下:

AA打印5次,BB打印10次,CC打印15次
 接着

AA 打印5次,BB打印10次,CC打印15次
……
循环10轮

大致通过JDK的支持梳理了下面的几种方案

  1. 通过线程不加锁定义状态变量(sleep)
  2. 通过synchronized关键字, 结合的wait与notifyAll
  3. Lock接口中 通过ReentrantLock的lock以及unlock
  4. Lock接口中 ReentrantLock结合Condition
  5. Lock接口中 Semaphore信号量方式

方法 / 步骤

一:通过线程不加锁定义状态变量(sleep)

通过线程配合sleep不加锁定义状态变量(非线程安全,可能会少打印)
启动三个线程,按照如下要求:
AA打印5此,BB打印10次,CC打印15次,一共进行10轮

public class ThreadOfSleep {

    private static String message = "AA";
    private static Integer loop = 1;

    public void TdSleep() {
        new Thread(() -> {
            int count = 1;
            while (count <= 5) {
                while (!message.equals("AA")) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(message);
                count = count + 1;
            }
            //while end
            message = "BB";
        }, "AA").start();

        new Thread(() -> {
            int count = 1;

                while (count <= 10) {
                    while (!message.equals("BB")) {
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //while end
                    System.out.println(message);
                    count = count + 1;

                }
                message = "CC";

        }, "BB").start();


        new Thread(() -> {
            int count = 1;
                while (count <= 15) {
                    while (!message.equals("CC")) {
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //while end
                    System.out.println(message);
                    count = count + 1;
                }
                message = "AA";
//                loop+=1;
//                System.out.println("current loop: "+ loop);

        }, "CC").start();

    }


    static class ThreadOfSleepTest {

        public static void main(String[] args) {
            ThreadOfSleep threadOfSleep = new ThreadOfSleep();
            for (int i = 1; i <= 10; i++) {
                threadOfSleep.TdSleep();
            }
        }
    }

二:通过synchronized关键字, 结合的wait与notifyAll

通过synchronized锁定 对象标志位的 wait与 notifyAll 定制线程间的通信
启动三个线程,按照如下要求:AA打印5次,BB打印10次,CC打印15次,一共进行10轮

public class ThreadOfSynchronized {


    private static String message = "AA";
    private static Integer loop = 1;
    private static Object lock = new Object();

    public void ThreadOfSync() {
        new Thread(() -> {
            int count = 1;

            synchronized (lock) {
                while (count <= 5) {
                    while (!message.equals("AA")) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(message);
                    count = count + 1;
                }
                //while end
                message = "BB";
                lock.notifyAll();
            }
            //sync end

        }, "AA").start();

        new Thread(() -> {
            int count = 1;

            synchronized (lock) {
                while (count <= 10) {
                    while (!message.equals("BB")) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //while end
                    System.out.println(message);
                    count = count + 1;

                }
                message = "CC";
                lock.notifyAll();
            }
            //sync end
        }, "BB").start();


        new Thread(() -> {
            int count = 1;
            synchronized (lock) {
                while (count <= 15) {
                    while (!message.equals("CC")) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //while end
                    System.out.println(message);
                    count = count + 1;
                }
                message = "AA";
                loop+=1;
                System.out.println("current loop: "+ loop);
                lock.notifyAll();
            }
            //sync end
        }, "CC").start();
    }


    public static class ThreadOfSynchronizedTest {
        public static void main(String[] args) {
            ThreadOfSynchronized threadOfSynchronized = new ThreadOfSynchronized();
            for (int i = 1; i <= 10; i++) {
                threadOfSynchronized.ThreadOfSync();
            }
        }
    }

}

三:通过ReentrantLock的lock以及unlock

通过ReentrantLock的lock以及unlock 进行定制线程
交替打印AA BB CC 十次 通过前面的线程 循环带出后面线程

public class ThreadOfRLock {

    // 通过JDK5中的Lock锁来保证线程的访问的互斥
    static Lock rLock = new ReentrantLock();
    //通过state的值来确定是否打印
    private static int state = 1;

    static class ThreadA extends Thread {
        @Override
        public void run() {
            for (int i = 1; i <= 10;) {
                try {
                    rLock.lock();
                    // 多线程并发,不能用if,必须用循环测试等待条件,避免虚假唤醒
                    while (state % 3 == 1) {
                        System.out.println(Thread.currentThread().getName()+"->> AA");
                        state++;
                        i++;
                    }
                } finally {
                    rLock.unlock();
                }
            }
        }
    }
    static class ThreadB extends Thread {
        @Override
        public void run() {
            for (int i = 1; i <= 10;) {
                try {
                    rLock.lock();
                    while (state % 3 == 2) {
                        System.out.println(Thread.currentThread().getName()+"->> BB");
                        state++;
                        i++;
                    }
                } finally {
                    rLock.unlock();
                }
            }
        }
    }
    static class ThreadC extends Thread {
        @Override
        public void run() {
            for (int i = 1; i <= 10;) {
                try {
                    rLock.lock();
                    while (state % 3 == 0) {
                        System.out.println(Thread.currentThread().getName()+"->> CC");
                        state++;
                        i++;

                    }
                } finally {
                    rLock.unlock();
                }
            }
        }
    }
    public static void main(String[] args) {
        new ThreadA().start();
        new ThreadB().start();
        new ThreadC().start();
    }

}

四:ReentrantLock结合Condition

启动三个线程,按照如下要求:
AA打印5此,BB打印10次,CC打印15次,一共进行10轮
每个线程添加一个标志位,是该标志位则执行操作,并且修改为下一个标志位,通知下一个标志位的线程
创建一个可重入锁private Lock lock = new ReentrantLock();
分别创建三个开锁通知private Condition c1 = lock.newCondition();
通过标志位Flag 和Condition 的wait 当前线程, sign(钥匙) 唤醒下一个线程 进行交替打印
public class CustomThread {

    //定义标志位
    private int flag = 1;

    //创建Lock锁
    Lock rLock = new ReentrantLock();

    //创建3个Condition 环境条件(Condition是绑定当前线程的)
    Condition c1 = rLock.newCondition();
    Condition c2 = rLock.newCondition();
    Condition c3 = rLock.newCondition();

    //打印5次,loop表示第几轮
    public void print5Count(int loop) throws InterruptedException {
        //上锁
        rLock.lock();
        try {
            //打印5次的标志位为1 打印10次标志位为2 打印15次的标志位为3, 非本次条件为等待
            while (flag != 1) {
                c1.await();
            }
            //业务处理
            for (int i = 1; i <= 5; i++) {
                System.out.println(Thread.currentThread().getName() + "::" + i + ": 轮数:" + loop);
            }
            //通知 修改标志位2
            flag = 2;
            //
            c2.signal();
        } finally {
            //释放锁
            rLock.unlock();
        }
    }

    //打印10次,loop表示第几轮
    public void print10Count(int loop) throws InterruptedException {
        //上锁
        rLock.lock();
        try {
            //打印5次的标志位为1 打印10次标志位为2 打印15次的标志位为3, 非本次条件为等待
            while (flag != 2) {
                c2.await();
            }
            //业务处理
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName() + "::" + i + ": 轮数:" + loop);
            }
            //通知 修改标志位2
            flag = 3;
            c3.signal();
        } finally {
            //释放锁
            rLock.unlock();
        }
    }


    //打印5次,loop表示第几轮
    public void print15Count(int loop) throws InterruptedException {
        //上锁
        rLock.lock();
        try {
            //打印5次的标志位为1 打印10次标志位为2 打印15次的标志位为3, 非本次条件为等待
            while (flag != 3) {
                c3.await();
            }
            //业务处理
            for (int i = 1; i <= 15; i++) {
                System.out.println(Thread.currentThread().getName() + "::" + i + ": 轮数:" + loop);
            }
            //通知 标志位为1的
            flag = 1;
            c1.signal();
        } finally {
            //释放锁
            rLock.unlock();
        }
    }

    public static class CustomThreadTest {

        public static void main(String[] args) {
            CustomThread customThread = new CustomThread();

            new Thread(() -> {
                for (int i = 1; i <= 10; i++) {
                    try {
                        customThread.print5Count(i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "AA").start();

            new Thread(() -> {
                for (int i = 1; i <= 10; i++) {
                    try {
                        customThread.print10Count(i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "BB").start();


            new Thread(() -> {
                for (int i = 1; i <= 10; i++) {
                    try {
                        customThread.print15Count(i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "CC").start();
        }
    }


}

五:Semaphore信号量方式

定义的全局变量,使用static
通过static Semaphore A = new Semaphore(1);,代表信号量为1
具体其代码块为:
semaphore.acquire(); 获取信号量
semaphore.release();释放信号量
public class ThreadOfSemaphore {


    // 以A开始的信号量,初始信号量数量为1
    private static Semaphore A = new Semaphore(1);
    // B、C信号量,A完成后开始,初始信号数量为0
    private static Semaphore B = new Semaphore(0);
    private static Semaphore C = new Semaphore(0);

    static class ThreadA extends Thread {
        @Override
        public void run() {
            try {
                for (int i = 1; i <= 10; i++) {
                    A.acquire();// A获取信号执行,A信号量减1,当A为0时将无法继续获得该信号量
                    System.out.println("AA");
                    B.release();// B释放信号,B信号量加1(初始为0),此时可以获取B信号量
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static class ThreadB extends Thread {
        @Override
        public void run() {
            try {
                for (int i = 1; i <= 10; i++) {
                    B.acquire();
                    System.out.println("BB");
                    C.release();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static class ThreadC extends Thread {
        @Override
        public void run() {
            try {
                for (int i = 1; i <= 10; i++) {
                    C.acquire();
                    System.out.println("CC");
                    A.release();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    public static void main(String[] args) {
        new ThreadA().start();
        new ThreadB().start();
        new ThreadC().start();
    }

}

参考资料 & 致谢

目录
相关文章
|
5天前
|
Java 调度
[Java]线程生命周期与线程通信
本文详细探讨了线程生命周期与线程通信。文章首先分析了线程的五个基本状态及其转换过程,结合JDK1.8版本的特点进行了深入讲解。接着,通过多个实例介绍了线程通信的几种实现方式,包括使用`volatile`关键字、`Object`类的`wait()`和`notify()`方法、`CountDownLatch`、`ReentrantLock`结合`Condition`以及`LockSupport`等工具。全文旨在帮助读者理解线程管理的核心概念和技术细节。
18 1
[Java]线程生命周期与线程通信
|
1天前
|
缓存 Java 调度
Java中的多线程编程:从基础到实践
【10月更文挑战第24天】 本文旨在为读者提供一个关于Java多线程编程的全面指南。我们将从多线程的基本概念开始,逐步深入到Java中实现多线程的方法,包括继承Thread类、实现Runnable接口以及使用Executor框架。此外,我们还将探讨多线程编程中的常见问题和最佳实践,帮助读者在实际项目中更好地应用多线程技术。
8 3
|
3天前
|
监控 安全 Java
Java多线程编程的艺术与实践
【10月更文挑战第22天】 在现代软件开发中,多线程编程是一项不可或缺的技能。本文将深入探讨Java多线程编程的核心概念、常见问题以及最佳实践,帮助开发者掌握这一强大的工具。我们将从基础概念入手,逐步深入到高级主题,包括线程的创建与管理、同步机制、线程池的使用等。通过实际案例分析,本文旨在提供一种系统化的学习方法,使读者能够在实际项目中灵活运用多线程技术。
|
1天前
|
缓存 安全 Java
Java中的多线程编程:从基础到实践
【10月更文挑战第24天】 本文将深入探讨Java中的多线程编程,包括其基本原理、实现方式以及常见问题。我们将从简单的线程创建开始,逐步深入了解线程的生命周期、同步机制、并发工具类等高级主题。通过实际案例和代码示例,帮助读者掌握多线程编程的核心概念和技术,提高程序的性能和可靠性。
6 2
|
2天前
|
Java
Java中的多线程编程:从基础到实践
本文深入探讨Java多线程编程,首先介绍多线程的基本概念和重要性,接着详细讲解如何在Java中创建和管理线程,最后通过实例演示多线程的实际应用。文章旨在帮助读者理解多线程的核心原理,掌握基本的多线程操作,并能够在实际项目中灵活运用多线程技术。
|
3天前
|
Java 数据处理 开发者
Java多线程编程的艺术:从入门到精通####
【10月更文挑战第21天】 本文将深入探讨Java多线程编程的核心概念,通过生动实例和实用技巧,引导读者从基础认知迈向高效并发编程的殿堂。我们将一起揭开线程管理的神秘面纱,掌握同步机制的精髓,并学习如何在实际项目中灵活运用这些知识,以提升应用性能与响应速度。 ####
19 3
|
5天前
|
Java
Java中的多线程编程:从入门到精通
本文将带你深入了解Java中的多线程编程。我们将从基础概念开始,逐步深入探讨线程的创建、启动、同步和通信等关键知识点。通过阅读本文,你将能够掌握Java多线程编程的基本技能,为进一步学习和应用打下坚实的基础。
|
6天前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
9 2
|
6天前
|
Java
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
18 2
|
6天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
15 2