[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();
    }

}

参考资料 & 致谢

目录
相关文章
|
24天前
|
Java 调度
[Java]线程生命周期与线程通信
本文详细探讨了线程生命周期与线程通信。文章首先分析了线程的五个基本状态及其转换过程,结合JDK1.8版本的特点进行了深入讲解。接着,通过多个实例介绍了线程通信的几种实现方式,包括使用`volatile`关键字、`Object`类的`wait()`和`notify()`方法、`CountDownLatch`、`ReentrantLock`结合`Condition`以及`LockSupport`等工具。全文旨在帮助读者理解线程管理的核心概念和技术细节。
36 1
[Java]线程生命周期与线程通信
|
10天前
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是实现线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件满足时被唤醒,从而确保数据一致性和同步。相比其他通信方式,如忙等待,这些方法更高效灵活。 示例代码展示了如何在生产者-消费者模型中使用这些方法实现线程间的协调和同步。
24 3
|
25天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
19 3
|
25天前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
16 2
|
25天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
28 2
|
25天前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
16 1
|
25天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
28 1
|
25天前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
34 1
|
25天前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
25 1
|
1月前
多线程通信和同步的方式有哪些?
【10月更文挑战第6天】
96 0