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

网友投稿 283 2022-09-09


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

目录wait-notifyjoin方式ReentrantLockReentrantLock+ConditionSemaphore

三个线程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 InterruptedExceptizKFwjzon {

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.uhttp://nlock();

}

}

}

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

}

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:NSSA区域以及OSPF路由汇总实验(ospf的nssa)
下一篇:天津***精密机械有限公司成功实施案例
相关文章

 发表评论

暂时没有评论,来抢沙发吧~