多线程之间的通信(等待唤醒机制、Lock 及其它线程的方法)

一、多线程之间的通信。

    就是多个线程在操作同一份数据, 但是操作的方法不同。

    如: 对于同一个存储块,其中有两个存储位:name   sex, 现有两个线程,一个向其中存放数据,一个打印其中的数据。

    为了解决上述问题中的安全问题(在存放线程进行存放操作的时候, 打印线程不能对共有数据进行操作),所以应当对两个线程
       操作共有数据的代码部分进行同步(使用synchronized(),来进行同步, 注意 :使用同一个对象作为同步锁。

二、等待唤醒机制。

    在上述案例实现过后运行,会发现:打印结果顺序并不是按照存放顺序进行的, 所以在此处就引入了等待唤醒机制。

        解释:就是在一个线程进行了规定操作后,就进入等待状态(wait()), 等待其他线程执行完他们的指定代码过后 再将其唤醒(notify())。
         在有多个线程进行等待时, 如果需要,可以使用 notifyAll()来唤醒所有的等待线程。

    所使用的方法(关键字):
          对象名.wait(); 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
          对象名.notify(); 唤醒在此对象监视器上等待的单个线程。
          对象名.notifyAll();唤醒在此对象监视器上等待的所有线程。

    
        注意:以上方法(关键字),都必须使用在同步中,因为其需要对持有该对象监视器所有权(锁)的线程进行操作。
      所以必须使用在同步中,因为只有同步中的线程才具有该对象监视器的所有权(锁),

        wait()和sleep()一样,都会抛出 InterruptedException 异常。

        而且,以上方法都定义在类:Object中

            是因为,这些方法在操作同步中的线程的时候,都必须标示其所操作线程所持有的锁(被该锁的对象调用),
          而只有同一个对象监视器下(同一个锁上的)的被等待线程,可以被持有该锁的线程唤醒,(无法唤醒不同锁上的线程)
            即: 等待和唤醒的必须是同一个对象的监视器下(同一个锁上)的线程。
          而锁可以是任意已近确定的对象, 能被任意对象调用的方法应当定义在 Object类中。

      监视器(锁): 同一个对象的监视器下(同一个锁上)的线程,一次只能执行一个:就是拥有监视器所有权(持有锁)的那一个线程。

三、接口Lock和 接口Condition

    注:在使用Lock 与Condition 时,必须引入其工具包:import java.util.concurrent.locks.*

             在jdk1.5以上,为了解决多个线程同时在对一份共享数据进行不同操作时出现的安全问题(要么陷入所有线程同时等待的状态、

          要么每次唤醒所有等待线程的问题),
              其为我们提供了新的 锁工具: Lock来替换 synchronized , 由 Lock对象所创建的绑定于该Lock对象的 condition对象

            替换了object类中的wait,notify操作。

    1、Lock接口, 已知实现该接口的类: ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock

      1)、 ReentrantLock(大体功能与 synchronized相当,不过可以绑定多个condition)

            构造方法:ReentrantLock() 创建一个 ReentrantLock 的实例。
                 ReentrantLock(boolean fair) 创建一个具有给定公平策略的 ReentrantLock

            常用方法: void lock() :获取一个锁。
                  void unlock() :释放该锁。
                  Condition newCondition() 返回用来与此 Lock 实例一起使用的 Condition 实例。

    2、Contition接口, 已知实现类:AbstractQueuedLongSynchronizer.ConditionObject, AbstractQueuedSynchronizer.ConditionObject(暂时不学习)

            常用方法:void await() 造成当前线程在接到信号或被中断之前一直处于等待状态。
                 void signal() 唤醒一个等待线程。
                 void signalAll() 唤醒所有等待线程。

        常见代码示例:
          class BoundedBuffer {
            final Lock lock = new ReentrantLock(); //创建一个Lock的子类对象 lock
            final Condition notFull = lock.newCondition(); //调用lock的newCondition方法,创建一个condition子类对象 notFull
            final Condition notEmpty = lock.newCondition(); //调用lock的newCondition方法,创建一个condition子类对象 notEmpty

            public void put(Object x) throws InterruptedException { //
                lock.lock(); //获取锁
                try {
                    while (判断语句)
                      notFull.await(); //判断成功,线程等待于notFull下。

                    操作代码

                    notEmpty.signal(); //唤醒notEmpty下的等待线程。
                } finally { //保证其后语句执行。
                     lock.unlock(); //释放锁。
                }
            }

            public Object take() throws InterruptedException {
                lock.lock();
                try {
                  while ()
                    notEmpty.await();
                  操作代码
                  notFull.signal();

                } finally {
                      lock.unlock();
                }
            }
          }

四、停止线程

      注:在旧版本的jdk中存在stop方法,但是在新版本中。此方法被过时。

      线程停止的原理: 当 run方法中的代码执行完毕过后,就自动停止该线程。

          1、当线程中是循环代码的时候, 只要控制住循环结束,就能够结束该线程。

              特殊情况:当线程中有wait()语句或者await()等语句时,会使得线程处于冻结状态, 让控制循环结束的代码或者标记无法执行或读取,

            那么线程就不会结束。

              当所有线程都陷入冻结,没有指定方法解除冻结时,就需要我们强制清除冻结状态,这样就可以操作标记使循环结束。

              Thread类中提供了这一方法: Interrupt()方法,(等待型语句本身会有一个InterrptedException异常的判断,

                      只要被Interrupt方法打断冻结,就会抛出这一异常, 我们就可以在异常处理语句中建立循环结束的标记)。

五、守护线程

    Thread类中有一个方法,调用该方法并传入 true ,能将该线程定义为 守护线程(后台线程),
        该方法是: void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。(调用时应当传入true)。

      注意: 1、将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
        (就是说,后台线程依赖于前台线程)

          2、该方法必须在启动线程前调用。

        示例: Thread t = new Thread();
            t.setDaemon(true);
            t.start();
          此时的线程 t 为守护线程。

六、Join方法。

    vpi解释:void join() 等待该线程终止。

    在线程A执行的时候,碰到了B线程.join方法时,A线程就会等待,等B线程执行完,才继续执行。
    (当在调用join方法时传入参数: long millis 时,表示 A线程等待B线程执行时间最长为millis毫秒。)

      join可以用来临时加入线程执行。

时间: 2024-11-03 22:00:41

多线程之间的通信(等待唤醒机制、Lock 及其它线程的方法)的相关文章

java基础知识回顾之java Thread类学习(八)--java多线程通信等待唤醒机制经典应用(生产者消费者)

 *java多线程--等待唤醒机制:经典的体现"生产者和消费者模型 *对于此模型,应该明确以下几点: *1.生产者仅仅在仓库未满的时候生产,仓库满了则停止生产. *2.消费者仅仅在有产品的时候才能消费,仓空则等待. *3.当消费者发现仓储没有产品可消费的时候,会唤醒等待生产者生产. *4.生产者在生产出可以消费的产品的时候,应该通知等待的消费者去消费. 下面先介绍个简单的生产者消费者例子:本例只适用于两个线程,一个线程生产,一个线程负责消费. 生产一个资源,就得消费一个资源. 代码如下: pub

java基础知识回顾之java Thread类学习(七)--java多线程通信等待唤醒机制(wait和notify,notifyAll)

1.wait和notify,notifyAll: wait和notify,notifyAll是Object类方法,因为等待和唤醒必须是同一个锁,不可以对不同锁中的线程进行唤醒,而锁可以是任意对象,所以可以被任意对象调用的方法,定义在Object基类中. wait()方法:对此对象调用wait方法导致本线程放弃对象锁,让线程处于冻结状态,进入等待线程的线程池当中.wait是指已经进入同步锁的线程,让自己暂时让出同步锁,以便使其他正在等待此锁的线程可以进入同步锁并运行,只有其它线程调用notify方

线程间通信——等待唤醒机制

线程间通信——等待唤醒机制,避免争夺同一资源: 锁对象可以是任意Object类的子类对象: 包子案例: 包子案例——生产者和消费者: 代码实现: 关键就是在于两个线程使用同一个锁对象! 这边是主程序调用这两个线程时候传入的同一个对象! 包子铺线程类——生产者: 其中,baozi类作为成员变量,并且重载了带参的构造方法: 锁对象为调用包子铺带参的构造方法传入的这个包子变量bz; 调用点wait方法必须是锁对象调用,锁对象调用wait方法之后当前线程就进入等待状态,另外一个线程此时应该是正在执行:

线程间的通信--等待唤醒机制

1.多个线程操作相同的资源,但是操作动作不同,所以存在安全问题例如: public class Test { public static void main(String[] args) { Resource r = new Resource(); Input in = new Input(r); Output out = new Output(r); Thread tin = new Thread(in); Thread tout = new Thread(out); tin.start();

JAVA-初步认识-第十四章-线程间通信-等待唤醒机制-代码优化

一. 上一节中的代码写的并不是很眼镜,如下图中的属性,应该都是私有的.我们不应该直接访问资源中的属性,是因为它具备了不安全性. 瞎赋值怎么办呢?为了可控,意味着资源里面的属性需要被私有化,并对外提供方法访问.因此上节中的代码要进行改写. 首先对资源描述类进行修改,至于为什么set方法中写有两个形参,是因为name和sex同时要做赋值,因此直接将它们定义在一起. 而且类中提供了直接输出name和sex的方法,后面的程序中就不需要写那么长的输出语句了.这个输出问题比较简单 关键问题在哪儿呢?如果这么

java之等待唤醒机制(线程之间的通信)

线程间通信 概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同.比如:线程A用来生成包子的,线程B用来吃包子的,包子可以理解为同一资源,线程A与线程B处理的动作,一个是生产,一个是消费,那么线程A与线程B之间就存在线程通信问题. 为什么要处理线程间通信: 多个线程并发执行时, 在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务,并且我们希望他们有规律的执行, 那么多线程之间需要一些协调通信,以此来帮我们达到多线程共同操作一份数据. 如何保证线程间通信有效利

Android-Java多线程通讯(生产者 消费者)&10条线程对-等待唤醒/机制的管理

上一篇博客 Android-Java多线程通讯(生产者 消费者)&等待唤醒机制 是两条线程(Thread-0 / Thread-1) 在被CPU随机切换执行: 而今天这篇博客是,在上一篇博客Android-Java多线程通讯(生产者 消费者)&等待唤醒机制 的基础上,扩大规模增加10条线程去执行 生产者 消费者: 注意:?? 上一篇博客是两条线程在执行(生产者 消费者)例如:当Thread-0 锁.wait(); 等待 冻结后,  Thread-1 锁.notify(); 唤醒的一定是 T

等待唤醒机制

等待唤醒机制 1.1 线程间通信 概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同. 比如:线程A用来生成包子的,线程B用来吃包子的,包子可以理解为同一资源,线程A与线程B处理的动作,一个是生产,一个是消费,那么线程A与线程B之间就存在线程通信问题. 为什么要处理线程间通信: 多个线程并发执行时, 在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务,并且我们希望他们有规律的执行, 那么多线程之间需要一些协调通信,以此来帮我们达到多线程共同操作一份数据.

java多线程(死锁,lock接口,等待唤醒机制)

一.Lock接口 常用方法 Lock提供了一个更加面对对象的锁,在该锁中提供了更多的操作锁的功能. 使用Lock接口,以及其中的lock()方法和unlock()方法替代同步,对电影院卖票案例中Ticket类进行如下代码修改: public class Ticket implements Runnable { //共100票 int ticket = 100; //创建Lock锁对象 Lock ck = new ReentrantLock(); @Override public void run