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 多线程循环打印内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
SpringBoot自动配置之自定义starter的实现代码
这篇文章主要介绍了SpringBoot自动配置之自定义starter的实现代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2020-10-10mybatis-plus团队新作mybatis-mate实现数据权限
本文主要介绍了mybatis-plus 团队新作 mybatis-mate 轻松搞定数据权限,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下2021-09-09解析Java中PriorityQueue优先级队列结构的源码及用法
优先级队列是一种队列结构,是0个或多个元素的集合,每个元素都有一个优先权,PriorityQueue被内置于JDK中,本文就来解析Java中PriorityQueue优先级队列结构的源码及用法.2016-05-05
最新评论