【多线程】线程间的通信

时间:2022-12-28 17:30:20

举一个例子来看一下线程间的通信。

示例:

子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着再回到主线程又循环100次。如此循环50次,请写出程序。

思路:

先找两个方法,分别执行子线程循环10次,主线程循环100次。并这两个同步。由外层控制,循环50次,并通过线程间通信得到,当子线程执行结束后,由主线程执行。并且主线程执行结束后,子线程也能开始执行。

实现:

先写了一个类,用于子主线程分别循环10次和100次。

class Business{
private boolean bShouldSub=true;
public synchronized void sub(int j){
for (int i = 0; i < 10; i++) {
System.out.println("sub thread sequece of: "+i);
}
}

public synchronized void main(int j){
for (int i = 0; i < 100; i++) {
System.out.println("main thread sequece of: "+i );
}
}

代码分析:
将两个方法都用synchronized 锁控制。都对this即同一个类的实例加锁,能保证两个方法同步。当子线程执行过程,主线程不会执行。
此时有外层再去控制循环50次。

public static void main(String[] args) {

final Business business =new Business();
new Thread(
new Runnable(){
@Override
public void run(){
for (int i = 0; i< 50; i++) {
business.sub(i);
}

}
}

).start();

for (int j = 0; j < 50; j++) {
business.main(j);
}
}
}

代码分析:
主方法能保证代码之间进行50次循环。但并不能保证是子线程和主线程交替执行。这时候就需要我们说的线程通信了。通过标志来标识当前是主还是子在执行,如果主执行,则让子等待。当主执行完之后通知子线程执行。主线程同理。
即修改业务逻辑代码。

class Business{
private boolean bShouldSub=true;
public synchronized void sub(int j){
//通过标识判断,如果当前不是子线程执行
while (!bShouldSub) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 0; i < 10; i++) {
System.out.println("sub thread sequece of: "+i);
}
bShouldSub=false;
this.notify();//唤醒正在等待的线程
}

public synchronized void main(int j){
while (bShouldSub) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for (int i = 0; i < 100; i++) {
System.out.println("main thread sequece of: "+i );
}
bShouldSub=true;
this.notify();
}
}

代码分析:
通过this.wait 和 this.notify相互通信。当子线程判断当前是主线程执行时,则子线程进入等待。等主线程执行完之后,会唤醒该子线程继续执行。

此外补充说明:
1.将用到共同数据(包括同步锁)或共同算法的若干个方法应该归在同一个类身上,这种设计正好体现了高内聚和程序的健壮性。
2.标志位判断时,用while不用if 。是防止程序被假唤醒。这样while还会再次确定一下“真的该我了么”。对程序多了一层保护。

线程间通信:


一)wait,notify,notifyAll 形式

wait,notify,(还有唤醒所有线程的notifyAll ) 都是object类提供的方法,不属于thread类。所以这三个方法都必须有同步监视器对象来调用。
①对于使用synchronized修饰的同步方法,因为该类的默认实例(this)就是同步监视器,所以可以再同步方法中直接调用这三个方法。
②对于使用synchronized修饰的同步代码块,同步监视器是synchronized后括号里的对象,所以必须使用该对象调用这三个方法。

 synchronized (obj) {
while (<condition does not hold>)
obj.wait();
... // Perform action appropriate to condition
}

wait():当前线程等待并释放对该同步监视器的锁定;可设定时间;需要由notify或notifyAll唤醒。

notify():随机唤醒此同步监视器上等待的一个线程。
notifyAll():唤醒此同步监视器上等待的所有线程。
该方式的弊端是,如果不使用synchronized同步,改用lock对象,则不存在隐式同步监视器,也就不能用该方式。

二)使用condition控制线程通信

该方式适用于用Lock锁时。通过调用Lock对象的newCondition()方法得到特定Lock实例的Condition实例。
方法和方式一基本对应。分别是:await(),signal(),signalAll();

public class Thread{
private final Lock lock=new ReentrantLock();
private final Condition connd=new lock.newCondition();
.....
pbulic void mathod1()
{
lock.lock();
if(flag)
{
cond.await();
}
else
{
...
cond.singalAll();
}
}
}

三)阻塞队列(BlockingQueue)控制线程通信

BlockingQueue特性:当生产者线程试图向BlockingQueue中放入元素时,如果该队列已满,则该线程被阻塞;当消费者线程试图从BlockingQueue中取元素,如果队列已空,则被阻塞。

总结:

本文介绍了三种线程间的通信方式。方式一适用于synchronized修饰时,采用wait,notify,notifyAll方式。方式二适用于用Lock修饰时,采用condition。方式三个人认为比较适用于生产者和消费者问题。欢迎补充,指正。