两个线程交替执行的几种方式

有个常见的面试题是这样,有2个字符串,一个字符串是1234567,另一个字符串是ABCDEFG,然后需要按1A2B3C4D5E6F7G或者A1B2C3D45E6F7G这种输出,就需要用到多线程交替执行来实现。本文列举了5种实现方式。

1.LockSupport

LockSupport是JDK1.6中引入的用来创建锁和其他同步工具类的基本线程阻塞原语。通过调用 LockSupport .park()和 LockSupport .unpark()实现线程的阻塞和唤醒。

import java.util.concurrent.locks.LockSupport;public class LockSupportTest {static Thread t1, t2 = null;public static void main(String[] args) {final char arrayA[] = "1234567".toCharArray();final char arrayB[] = "ABCDEFG".toCharArray();t1 = new Thread(() -> {for (char charA : arrayA) {System.out.print(charA);LockSupport.unpark(t2);LockSupport.park();}}, "t1");t1.start();t2 = new Thread(() -> {for (char charB : arrayB) {LockSupport.park();System.out.print(charB);LockSupport.unpark(t1);}}, "t2");t2.start();}
}

2.synchronized结合notify、wait

Object中的notify方法用来从等待队列里唤醒一个线程,wait方法将当前线程放入等待队列,同时让出锁。

public class SyncNotifyWaitTest {public static void main(String[] args) {final char arrayA[] = "1234567".toCharArray();final char arrayB[] = "ABCDEFG".toCharArray();Object o = new Object();Runnable runnable = () -> {synchronized (o) {for (char charA : arrayA) {System.out.print(charA);try {o.notify();o.wait();} catch (InterruptedException e) {e.printStackTrace();}}o.notify();}};Runnable runnable1 = () -> {synchronized (o) {for (char charB : arrayB) {try {System.out.print(charB);o.notify();o.wait();} catch (InterruptedException e) {e.printStackTrace();}}o.notify();}};new Thread(runnable).start();new Thread(runnable1).start();}}

3.Lock结合Condition

Condition对象中的signal方法作用和Object的notify方法作用一样,await方法和Object的wait方法作用一样。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockConditionTest {public static void main(String[] args) {final char arrayA[] = "1234567".toCharArray();final char arrayB[] = "ABCDEFG".toCharArray();Lock lock = new ReentrantLock();Condition condition = lock.newCondition();Runnable runnable = () -> {try {lock.lock();for (char charA : arrayA) {System.out.print(charA);condition.signal();condition.await();}condition.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}};Runnable runnable1 = () -> {try {lock.lock();for (char charB : arrayB) {System.out.print(charB);condition.signal();condition.await();}condition.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}};new Thread(runnable).start();new Thread(runnable1).start();}
}

4.CountDownLatch

 CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成了任务,然后在CountDownLatch上等待的线程就可以恢复执行任务。

通过调用await方法使得线程处于等待状态,然后线程执行完任务后,调用countDown方法,使得计数器的值减一。

import java.util.concurrent.CountDownLatch;public class CountDownLatchTest {public static void main(String[] args) {CountDownLatch countDownLatch = new CountDownLatch(1);Object o = new Object();final char arrayA[] = "1234567".toCharArray();final char arrayB[] = "ABCDEFG".toCharArray();Runnable runnable = () -> {synchronized (o) {for (char charA : arrayA) {System.out.print(charA);countDownLatch.countDown();o.notify();try {o.wait();} catch (InterruptedException e) {e.printStackTrace();}}o.notify();}};Runnable runnable1 = () -> {try {countDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}synchronized (o) {for (char charB : arrayB) {System.out.print(charB);try {o.notify();o.wait();} catch (InterruptedException e) {e.printStackTrace();}}o.notify();}};new Thread(runnable).start();new Thread(runnable1).start();}
}

5.阻塞队列LinkedTransferQueue

LinkedTransferQueue 消费者线程获取取数据时:调用take方法

如果队列不为空,则直接取走数据,若队列为空则消费者线程会生成一个占位虚拟节点(节点元素为null)入队,并等待在这个节点上,后面生产者线程请求添加数据时,会从单向链表的head节点开始遍历,如果发现某个节点是一个取数请求任务类型的节点(即item为null),生产者线程就不入队了,直接就将元素填充到该节点,并唤醒该节点等待的消费者线程,被唤醒的消费者线程取走元素 ;
LinkedTransferQueue 生产者线程传递数据时:调用transfer方法

当有消费者线程阻塞等待时,调用transfer方法的生产者线程不会将元素存入队列,而是直接将元素传递给消费者,并唤醒阻塞的线程;
如果调用transfer方法的生产者线程发现没有正在等待的消费者线程,则这个生产者请求创建一个节点,这个节点将会被添加到当前链表的末尾将数据入队,然后会阻塞等待,直到有一个消费者线程来获取该元素。

import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TransferQueue;public class TransferQueneTest {public static void main(String[] args) {final char arrayA[] = "1234567".toCharArray();final char arrayB[] = "ABCDEFG".toCharArray();TransferQueue transferQueue = new LinkedTransferQueue<>();Runnable runnable = () -> {for (char charA : arrayA) {try {System.out.print(transferQueue.take());transferQueue.transfer(charA);} catch (InterruptedException e) {e.printStackTrace();}}};Runnable runnable1 = () -> {for (char charB : arrayB) {try {transferQueue.transfer(charB);System.out.print(transferQueue.take());} catch (InterruptedException e) {e.printStackTrace();}}};new Thread(runnable).start();new Thread(runnable1).start();}
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部