线程间通信的几种实现方式
首先,SMS线程之间的通信有两种模型:共享内存和消息传递。下面的方法基本都是这两个模型实现的。我们来分析一个基本的面试问题:
题目:有两个线程A和B,线程A依次将元素“abc”字符串添加到一个集合中,共添加十次。添加到第五次时,希望线程B能够收到线程A的通知,然后由B线程进行相关的业务操作。
方式一:使用volatile关键字
基于volatile关键字的线程间通信是使用共享内存的思想,大致意思是多个线程同时监听一个变量。当变量发生变化时,线程可以感知并执行相应的业务。这也是最简单的方法。
public class TestSync {
// 定义一个共享变量来实现通信,它需要是volatile修饰,否则线程不能及时感知
static volatile boolean notice = false;
public static void main(String[] args) {
List<String> list = new ArrayList<>();
// 实现线程A
Thread threadA = new Thread(() -> {
for (int i = 1; i <= 10; i++) {
list.add("abc");
System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:"+list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5)
notice = true;
}
});
// 实现线程B
Thread threadB = new Thread(() -> {
while (true) {
if (notice) {
System.out.println("线程B收到通知,开始执行自己的业务...");
break;
}
}
});
// 需要先启动线程B
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 再启动线程A
threadA.start();
}
}
运行结果为:
方式二:使用Object类的wait()和notify()方法
众所周知,Object类提供了线程间通信的方法:wait()、notify()、notifyaAl(),它们是多线程通信的基础,而这种实现的思路自然是跨线程的线程通信。
注意:wait和notify必须和synchronized结合使用,wait方法释放锁,notify方法不释放锁。
public class TestSync {
public static void main(String[] args) {
// 定义一个锁对象
Object lock = new Object();
List<String> list = new ArrayList<>();
// 实现线程A
Thread threadA = new Thread(() -> {
synchronized (lock) {
for (int i = 1; i <= 10; i++) {
list.add("abc");
System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:"+
list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5)
lock.notify();// 唤醒B线程
}
}
});
// 实现线程B
Thread threadB = new Thread(() -> {
while (true) {
synchronized (lock) {
if (list.size() != 5) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程B收到通知,开始执行自己的业务...");
}
}
});
// 需要先启动线程B
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 再启动线程A
threadA.start();
}
}
运行结果为:
从打印结果截图可以看出,线程A发送notify()唤醒通知后,线程B处理完自己线程的业务后开始执行。这也正好说明notify()方法没有释放锁,而wait()方法释放了锁。
方式三:使用JUC工具类 CountDownLatch
jdk1.5之后,java.util.concurrent包下提供了很多并发编程相关的工具,简化了我们并发编程代码的编写。 ***CountDownLatch***基于AQS框架,相当于维护一个线程共享变量状态。
public class TestSync {
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(1);
List<String> list = new ArrayList<>();
// 实现线程A
Thread threadA = new Thread(() -> {
for (int i = 1; i <= 10; i++) {
list.add("abc");
System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:"+list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5)
countDownLatch.countDown();
}
});
// 实现线程B
Thread threadB = new Thread(() -> {
while (true) {
if (list.size() != 5) {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程B收到通知,开始执行自己的业务...");
break;
}
});
// 需要先启动线程B
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 再启动线程A
threadA.start();
}
}
运行结果为:
方法四:使用ReentrantLock结合Condition
public class TestSync {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
List<String> list = new ArrayList<>();
// 实现线程A
Thread threadA = new Thread(() -> {
lock.lock();
for (int i = 1; i <= 10; i++) {
list.add("abc");
System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:"+list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5)
condition.signal();
}
lock.unlock();
});
// 实现线程B
Thread threadB = new Thread(() -> {
lock.lock();
if (list.size() != 5) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程B收到通知,开始执行自己的业务...");
lock.unlock();
});
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadA.start();
}
}
运行结果为:
显然,这种方法不太好用,代码编写复杂,线程B被A唤醒后不能立即执行,因为它没有获取锁,也就是A唤醒后没有释放锁手术。这个方法和Object的wait()和notify()是一样的。
方式五:基于LockSupport实现线程间的阻塞和唤醒
LockSupport 是一个非常灵活的线程间阻塞和唤醒工具。不需要关注是等待线程先运行还是唤醒线程先运行,但是必须知道线程的名字。
public class TestSync {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
// 实现线程B
final Thread threadB = new Thread(() -> {
if (list.size() != 5) {
LockSupport.park();
}
System.out.println("线程B收到通知,开始执行自己的业务...");
});
// 实现线程A
Thread threadA = new Thread(() -> {
for (int i = 1; i <= 10; i++) {
list.add("abc");
System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:"+list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5)
LockSupport.unpark(threadB);
}
});
threadA.start();
threadB.start();
}
}
运行结果:
python学习网,大量的免费
,欢迎在线学习!
本文为原创文章,版权归知行编程网所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ 如何在 python tkinter 中使用绝对布局?11/01
- ♥ 如何在python3中清除屏幕10/05
- ♥ python运行图标消失怎么办09/03
- ♥ python用什么IDE09/03
- ♥ Python threading实现线程处理的过程12/16
- ♥ 如何在python中表示一万10/22
内容反馈