Java五种方式实现多线程循环打印问题

 更新时间:2021年12月14日 10:57:37   作者:北城酒卿  
本文主要介绍了Java五种方式实现多线程循环打印问题,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

三个线程T1、T2、T3轮流打印ABC,打印n次,如ABCABCABCABC…
N个线程循环打印1-100…

wait-notify

循环打印问题可以通过设置目标值,每个线程想打印目标值,如果拿到锁后这次轮到的数不是它想要的就进入wait

class Wait_Notify_ABC {
    private int num;
    private static final Object Lock = new Object();

    private void print_ABC(int target) {
        synchronized (Lock) {
            //循环打印
            for (int i = 0; i < 10; i++) {
                while (num % 3 != target) {
                    try {
                        Lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                num++;
                System.out.print(Thread.currentThread().getName());
                Lock.notifyAll();
            }
        }
    }

    public static void main(String[] args) {
        Wait_Notify_ABC wait_notify_abc = new Wait_Notify_ABC();
        new Thread(() -> {
            wait_notify_abc.print_ABC(0);
        }, "A").start();
        new Thread(() -> {
            wait_notify_abc.print_ABC(1);
        }, "B").start();
        new Thread(() -> {
            wait_notify_abc.print_ABC(2);
        }, "C").start();
    }
}

打印1-100问题可以理解为有个全局计数器记录当前打印到了哪个数,其它就和循环打印ABC问题相同。

class Wait_Notify_100 {

    private int num;
    private static final Object LOCK = new Object();
    private int maxnum = 100;

    private void printABC(int targetNum) {
        while (true) {
            synchronized (LOCK) {
                while (num % 3 != targetNum) {
                    if (num >= maxnum) {
                        break;
                    }
                    try {
                        LOCK.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if (num >= maxnum) {
                    break;
                }
                num++;
                System.out.println(Thread.currentThread().getName() + ": " + num);
                LOCK.notifyAll();
            }
        }

    }

    public static void main(String[] args) {
        Wait_Notify_100 wait_notify_100 = new Wait_Notify_100();
        new Thread(() -> {
            wait_notify_100.printABC(0);
        }, "thread1").start();
        new Thread(() -> {
            wait_notify_100.printABC(1);
        }, "thread2").start();
        new Thread(() -> {
            wait_notify_100.printABC(2);
        }, "thread3").start();
    }
}

join方式

一个线程内调用另一个线程的join()方法可以让另一个线程插队执行,比如Main方法里调用了A.join(),那么此时cpu会去执行A线程中的任务,执行完后再看Main是否能抢到运行权。所以对于ABC,我们可以对B说让A插队,对C说让B插队

class Join_ABC {
    static class printABC implements Runnable {
        private Thread beforeThread;

        public printABC(Thread beforeThread) {
            this.beforeThread = beforeThread;
        }

        @Override
        public void run() {
            if (beforeThread != null) {
                try {
                    beforeThread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.print(Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            Thread t1 = new Thread(new printABC(null), "A");
            Thread t2 = new Thread(new printABC(t1), "B");
            Thread t3 = new Thread(new printABC(t2), "C");
            t1.start();
            t2.start();
            t3.start();
            Thread.sleep(100);
        }
    }
}

ReentrantLock

同理,synchronized和reentrantlock都是我们常用的加锁方式,不过后者可以中断,可以实现公平锁,可以使用condition…但是需要我们手动释放锁。jdk8后二者性能差不多,毕竟synchronized有锁升级的过程嘛。

class ReentrantLock_ABC {

    private int num;   
    private Lock lock = new ReentrantLock();

    private void printABC(int targetNum) {
        for (int i = 0; i < 100; ) {
            lock.lock();
            if (num % 3 == targetNum) {
                num++;
                i++;
                System.out.print(Thread.currentThread().getName());
            }
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        Lock_ABC lockABC = new Lock_ABC();

        new Thread(() -> {
            lockABC.printABC(0);
        }, "A").start();

        new Thread(() -> {
            lockABC.printABC(1);
        }, "B").start();

        new Thread(() -> {
            lockABC.printABC(2);
        }, "C").start();
    }
}

ReentrantLock+Condition

以上方式如果线程抢到锁后发现自己无法执行任务,那么就释放,然后别的线程再抢占再看是不是自己的…这种方式比较耗时,如果我们能实现精准唤醒锁呢,即A完成任务后唤醒它的下一个即B,这就用到我们的Condition啦

class ReentrantLock_Condition_ABC {

    private int num;
    private static Lock lock = new ReentrantLock();
    private static Condition c1 = lock.newCondition();
    private static Condition c2 = lock.newCondition();
    private static Condition c3 = lock.newCondition();

    private void printABC(int targetNum, Condition currentThread, Condition nextThread) {
        for (int i = 0; i < 100; ) {
            lock.lock();
            try {
                while (num % 3 != targetNum) {
                    currentThread.await();  //阻塞当前线程
                }
                num++;
                i++;
                System.out.print(Thread.currentThread().getName());
                nextThread.signal();    //唤醒下一个线程
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        ReentrantLock_Condition_ABC reentrantLockConditionAbc = new ReentrantLock_Condition_ABC();
        new Thread(() -> {
            reentrantLockConditionAbc.printABC(0, c1, c2);
        }, "A").start();
        new Thread(() -> {
            reentrantLockConditionAbc.printABC(1, c2, c3);
        }, "B").start();
        new Thread(() -> {
            reentrantLockConditionAbc.printABC(2, c3, c1);
        }, "C").start();
    }
}

Semaphore

小伙伴们有没有想到过,在生产者消费者模型中我们有哪几种实现方式呢?wait\notify,ReentrantLock,Semaphone,阻塞队列,管道输入输出流。
对的就是Semaphone。
Semaphore有acquire方法和release方法。 当调用acquire方法时线程就会被阻塞,直到获得许可证为止。 当调用release方法时将向Semaphore中添加一个许可证。如果没有获取许可证的线程, Semaphore只是记录许可证的可用数量。
使用Semaphore也可以实现精准唤醒。

class SemaphoreABC {

    private static Semaphore s1 = new Semaphore(1); //因为先执行线程A,所以这里设s1的计数器为1
    private static Semaphore s2 = new Semaphore(0);
    private static Semaphore s3 = new Semaphore(0);

    private void printABC(Semaphore currentThread, Semaphore nextThread) {
        for (int i = 0; i < 10; i++) {
            try {
                currentThread.acquire();       //阻塞当前线程,即信号量的计数器减1为0
                System.out.print(Thread.currentThread().getName());
                nextThread.release();          //唤醒下一个线程,即信号量的计数器加1
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SemaphoreABC printer = new SemaphoreABC();
        new Thread(() -> {
            printer.printABC(s1, s2);
        }, "A").start();
        Thread.sleep(100);
        new Thread(() -> {
            printer.printABC(s2, s3);
        }, "B").start();
        Thread.sleep(100);
        new Thread(() -> {
            printer.printABC(s3, s1);
        }, "C").start();
    }
}

到此这篇关于Java五种方式实现多线程循环打印问题的文章就介绍到这了,更多相关Java 多线程循环打印内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Kafka中Producer和Consumer的作用详解

    Kafka中Producer和Consumer的作用详解

    这篇文章主要介绍了Kafka中Producer和Consumer的作用详解,Kafka是一个分布式的流处理平台,它的核心是消息系统,Producer是Kafka中用来将消息发送到Broker的组件之一,它将消息发布到主题,并且负责按照指定的分区策略将消息分配到对应的分区中,需要的朋友可以参考下
    2023-12-12
  • Java读写文件创建文件夹多种方法示例详解

    Java读写文件创建文件夹多种方法示例详解

    这篇文章主要介绍了Java读写文件创建文件夹等多种操作的方法,大家参考使用吧
    2013-11-11
  • Java Lambda 表达式源码解析

    Java Lambda 表达式源码解析

    这篇文章主要介绍了Java Lambda在JVM中是如何实现的,感兴趣的小伙伴一起来了解了解
    2021-08-08
  • Java实现冒泡排序算法及对其的简单优化示例

    Java实现冒泡排序算法及对其的简单优化示例

    这篇文章主要介绍了Java实现冒泡排序算法及对其的简单优化示例,冒泡排序的最差时间复杂度为O(n^2),最优时间复杂度为O(n),存在优化的余地,需要的朋友可以参考下
    2016-05-05
  • Springboot使用redis实现接口Api限流的示例代码

    Springboot使用redis实现接口Api限流的示例代码

    本文主要介绍了Springboot使用redis实现接口Api限流的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • 浅谈web项目读取classpath路径下面的文件

    浅谈web项目读取classpath路径下面的文件

    这篇文章主要介绍了浅谈web项目读取classpath路径下面的文件,具有一定借鉴价值,需要的朋友可以参考下
    2018-01-01
  • SpringBoot获取Request请求的三种方式小结

    SpringBoot获取Request请求的三种方式小结

    本文介绍了SpringBoot中获取Request对象的三种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-12-12
  • 阿里云OSS域名配置及简单上传的示例代码

    阿里云OSS域名配置及简单上传的示例代码

    这篇文章主要介绍了阿里云OSS域名配置及简单上传的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • quarzt定时调度任务解析

    quarzt定时调度任务解析

    这篇文章主要介绍了quarzt定时调度任务,具有一定参考价值,需要的朋友可以了解下。
    2017-12-12
  • Spring Boot整合ElasticSearch实现多版本兼容的方法详解

    Spring Boot整合ElasticSearch实现多版本兼容的方法详解

    简单说,ElasticSearch(简称 ES)是搜索引擎,是结构化数据的分布式搜索引擎。下面这篇文章主要给大家介绍了关于Spring Boot整合ElasticSearch实现多版本兼容的相关资料,需要的朋友可以参考借鉴,下面来一起看看吧
    2018-05-05

最新评论