多线程中线程间如何通信

除了使用synchronized来对代码块和方法进行同步外,jdk1.5之后还有一种Lock同步锁的方式进行同步:使用lock.lock()来进行加锁,使用lock.unlock()方法来释放锁,既然可以使用lock来代替synchronized,那么如何进行处理synchronized与wait()、notify()、notifyAll()的线程通信机制呢,在使用lock时使用Condition来进行线程通信。

下面来使用两种方式来分别展示一下线程间通信。

使用Object的wait()、notify进行线程通信

阻塞

调用wait()方法,将阻塞挂起等待其他线程的通知,进入该对象的线程等待池中,调用wait()方法之前,线程必须要获得该对象的对象级别锁,即只能在同步块或者同步方法中使用,否则会报IllegalMonitorStateException异常。

唤醒

调用notify()方法和notifyAll()方法来通知那些等待该对象锁的其他线程,(如果有多个线程等待,notify会任意挑选一个线程来进行通知,notifyAll会对所有该对象上由于调用wait方法而被挂起的线程进行通知,),唤醒之后的线程会进入该对象的锁池中,需要竞争到对象的监视器锁才可以继续执行,调用notify()方法和notifyAll()方法之前,线程必须要获得该对象的对象级别锁,即只能在同步块或者同步方法中使用。

除了调用notify()方法和notifyAll()方法来唤醒线程之外,还可以使用中断的方式来使得线程在wait()处抛出InterruptedException异常而返回并终止。

如果使用的是wait(timeout)方法进行阻塞的,那么也会由于在指定时间内没有被唤醒而因为超时返回。

/**
 * wait()/notify()/notifyAll()是Object的方法
 * 只能在synchronized方法或者代码块中使用,否则会报IllegalMonitorStateException异常
 * 交替打印1-100
 */
public class TestThreadSignal {
    public static void main(String[] args) {
        PrintRunnable runnable = new PrintRunnable();
        new Thread(runnable,"线程一").start();
        new Thread(runnable,"线程二").start();
    }
}
class PrintRunnable implements Runnable{
    int num = 1;
    @Override
    public void run() {
        while (true){
            synchronized (this){
                notify();// 唤醒wait的线程
                if(num <= 100){
                    System.out.println(Thread.currentThread().getName() + "打印" + num++);
                } else {
                    break;
                }
                try {
                    wait(); // 释放当前的锁
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

虚假唤醒现象

有时在没有被其他线程调用notify/notifyAll,或者没有被中断,或者没有等待超时,也有可能会从挂起状态变为运行状态(虚假唤醒),虽然概率很低,但是最好还是使用while(条件)来一直查看条件是否满足。

synchronized(obj){
  while(条件){
    obj.wait();
  }
}
使用condition的await()、signal()进行线程通信
/**
 * 使用lock和condition来进行线程通信
 * condition中的await()、signal()、signalAll()方法分别对应于Object中的wait()、notify()、notifyAll()方法
 * 两个线程交替打印1-100
 */
public class TestThreadSignalForLock {
    public static void main(String[] args) {
        PrintRunnableForLock runnable = new PrintRunnableForLock();
        new Thread(runnable,"线程一").start();
        new Thread(runnable,"线程二").start();
    }
}
class PrintRunnableForLock implements Runnable{
    int num = 1;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    @Override
    public void run() {
        while (true){
           try{
               lock.lock();
               condition.signal();// 唤醒线程
               if(num <= 100){
                   System.out.println(Thread.currentThread().getName() + "打印" + num++);
               } else {
                   break;
               }
               try {
                   condition.await(); // 释放当前的锁
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           } finally {
               lock.unlock();
           }

        }
    }
}

 TestThreadSignalForLock 类中的 main 方法创建了一个 PrintRunnableForLock 对象的实例,并用这个实例创建了两个线程(线程一和线程二)。PrintRunnableForLock 类实现了 Runnable 接口,并重写了 run 方法。这个类有一个 num 变量用于记录当前要打印的数字,一个 Lock 对象用于同步,以及一个 Condition 对象用于线程间的通信。 在run 方法中,每个线程首先获取锁,然后调用 condition.signal() 唤醒其他等待的线程。接着,如果 num 小于等于100,就打印当前的数字,并将 num 加1。之后,线程会尝试调用 condition.await() 进入等待状态,并释放锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值