【JAVA学习之路 | 提高篇】线程的通信

简介: 【JAVA学习之路 | 提高篇】线程的通信

1.需求(为什么需要线程通信)

当我们需要多个线程完成同一任务时,并且希望他们有规律的执行,那么多线程之间需要一些通信机制,并且可以协调他们的工作,以此实现多个线程共同操作共享数据.

例 : A做包子,B吃包子,包子相当于共享操作的数据,B必须等到A做好才能吃,那么线程AB间就需要通信.即等待唤醒机制.

2.等待唤醒机制

这是多线程的一种协同的机制.谈到线程我们常常想到是多个线程竞争一把锁.但不完全是这样.线程之间也可以实现协同.


在一个线程满足某个条件时,就进入等待状态(wait),等待其他线程进行到某处指定代码时,将等待状态的线程唤醒(notify).可以指定wait的时间,过了这个时间可以自动唤醒@也可以使用notifyAll来唤醒所有等待的线程.

  • wait : 线程不在参与活动,将释放CPU调度.进入wait set中,不会浪费CPU资源.也不会去竞争锁.此时线程状态为WAITING或者是TIME_WAITING.他等待其他线程执行某个操作(notify/wait(time)执行了time的时间),才能够将其从wait set中释放出来,重新参与CPU调度.
  • notify : 选取待在wait set中的线程将其释放重新参与CPU调度.
  • notifyAll : 将全部待在wait set中的线程全部释放参与CPU调度.

3.例 : 交替打印出输出的一百以内的数.

解释 : 调用start方法启动线程.假设是线程1(当然也可能是线程2)进入while语句并握住锁,进行输出语句后,调用wait()释放锁,线程2握住锁,进行到notify()唤醒线程1重新参与CPU调度......

public class WaitNotifyTest {
    public static void main(String[] args) {
        WaitNotify w = new WaitNotify();
        Thread t1 = new Thread(w, "线程-1");
        Thread t2 = new Thread(w, "线程-2");
        t1.start();
        t2.start();
    }
}
class WaitNotify implements Runnable {
    int change = 100;
    Object obj = new Object();
    @Override
    public void run() {
        while (true) {
            synchronized(obj) {
                obj.notify();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (change > 0) {
                    System.out.println(Thread.currentThread().getName() + "\t" + change);
                    change--;
                }
                try{
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
 
        }
    }
}
 
控制台
线程-1  100
线程-2  99
线程-1  98
线程-2  97
线程-1  96
线程-2  95
线程-1  94

注 :

  • 这三个方法的使用必须都要在同步代码块或者同步方法中.
  • 此三个方法的调用者必须是同步监视器,否则会触发异常.
  • 按住Ctrl键,可以查看该三个方法的源码源码.


public final native void notify();
public final void wait() throws InterruptedException {
    wait(0L);
}

此三个方法都声明在Object类中,Ctrl+F12,查看Object类.

  • 因为同步监视器可以是任何对象(保证线程安全性需要满足唯一),由于该三个方法是Object类中的方法,所以同步监视器可以调用.

4.wait()与sleep()的区别.

(1). 查看源码 :

public static native void sleep(long millis) throws InterruptedException;
public final void wait() throws InterruptedException {
        wait(0L);
    }

(2) 相同点 :

一旦执行,都可以阻塞线程.

(3) 不同点 :

  • 声明的位置不同 : sleep()方法声明在Thread类中,而且是静态的方法.wait()方法声明在Object类中.
  • 使用的场景不同 : sleep()可以声明在任何所需要的场景.wait()必须声明在同步代码块或同步方法中(因为该写方法必须通过同步监视器调用).
  • CPU调度 : 使用sleep()并不会释放CPU调度,只是线程阻塞.而wait()阻塞线程,并且释放该线程的CPU调度.
  • 结束阻塞方式 : sleep(time)方法在时间time之后结束阻塞.wait(time)方法在时间time之后或者notify/notifyAll结束阻塞.
相关文章
|
3天前
|
缓存 NoSQL Java
Java高并发实战:利用线程池和Redis实现高效数据入库
Java高并发实战:利用线程池和Redis实现高效数据入库
16 0
|
1天前
|
Java 调度
【Java基础】 线程状态转化
Java线程状态转化
13 2
|
3天前
|
监控 Java API
Java 程序设计 第八章 线程
Java 程序设计 第八章 线程
|
3天前
|
存储 安全 Java
Java多线程编程--JUC
Java多线程编程
|
4天前
|
Java API
详细探究Java多线程的线程状态变化
Java多线程的线程状态主要有六种:新建(NEW)、可运行(RUNNABLE)、阻塞(BLOCKED)、等待(WAITING)、超时等待(TIMED_WAITING)和终止(TERMINATED)。线程创建后处于NEW状态,调用start()后进入RUNNABLE状态,表示准备好运行。当线程获得CPU资源,开始执行run()方法时,它处于运行状态。线程可以因等待锁或调用sleep()等方法进入BLOCKED或等待状态。线程完成任务或发生异常后,会进入TERMINATED状态。
|
4天前
|
存储 安全 Java
Java多线程中线程安全问题
Java多线程中的线程安全问题主要涉及多线程环境下对共享资源的访问可能导致的数据损坏或不一致。线程安全的核心在于确保在多线程调度顺序不确定的情况下,代码的执行结果始终正确。常见原因包括线程调度随机性、共享数据修改以及原子性问题。解决线程安全问题通常需要采用同步机制,如使用synchronized关键字或Lock接口,以确保同一时间只有一个线程能够访问特定资源,从而保持数据的一致性和正确性。
|
4天前
|
监控 安全 Java
Java多线程的使用
Java多线程允许程序同时执行多个任务,提高了系统的整体性能和响应速度。通过创建Thread类或其子类的实例,或使用Runnable接口,Java开发者可以定义并发执行的代码段。多线程在处理复杂任务、资源共享、网络通信等方面具有显著优势,但也需要注意线程安全、同步和死锁等问题。Java提供了丰富的API和工具来处理这些并发问题,使多线程编程更加高效和可靠。
|
4天前
|
Java
JAVA高级部分学习小结(2023.11.4)
JAVA高级部分学习小结(2023.11.4)
|
4天前
|
API
java-多线程-CountDownLatch(闭锁) CyclicBarrier(栅栏) Semaphore(信号量)-
java-多线程-CountDownLatch(闭锁) CyclicBarrier(栅栏) Semaphore(信号量)-
10 1
|
4天前
|
安全 Java 程序员
Java基础18-一文搞懂Java多线程使用方式、实现原理以及常见面试题(二)
Java基础18-一文搞懂Java多线程使用方式、实现原理以及常见面试题(二)
26 4