当前位置: 首页 > news >正文

建设部网站八大员查询广西seo经理

建设部网站八大员查询,广西seo经理,惠州关键词排名优化,手机网站用什么程序做目录 〇、先总结一下这三个方法带来的Java线程状态变化 一、obj.wait() 1.1 作用 1.2 使用前需要持有线程共享对象的锁 1.3 使用技巧 二、obj.notify(All)() 1.1 notify() 方法 1.1.1 调用notify()或notifyAll()不会释放线程的锁 1.2 notifyAll() 方法 1.3 使用技巧 三、使用实…

目录

〇、先总结一下这三个方法带来的Java线程状态变化

一、obj.wait()

1.1 作用

1.2 使用前需要持有线程共享对象的锁

1.3 使用技巧

二、obj.notify(All)()

1.1 notify() 方法

1.1.1 调用notify()或notifyAll()不会释放线程的锁

1.2 notifyAll() 方法

1.3 使用技巧

三、使用实例

四、wait()/notify()/notifyAll() 为什么定义在 Object 类中?


wait()、notify/notifyAll() 方法都是Object的本地final方法,无法被重写。

〇、先总结一下这三个方法带来的Java线程状态变化

当Java线程调用wait()方法后,该线程会进入等待队列,并且会释放占用的锁资源。线程状态会变为WAITING或TIMED_WAITING。该线程不会被挂起到外存,而是在内存中等待被唤醒。线程等待的条件通常是由其他线程调用notify()或notifyAll()方法来唤醒该线程。

当线程被唤醒时,它会重新尝试获取锁资源并从wait()方法返回。线程状态会变为BLOCKED,直到它获得了锁资源为止。如果成功获取锁资源,线程状态会变为RUNNABLE,然后可以继续执行。如果获取锁资源失败,则线程会继续等待,并且状态会维持在BLOCKED或WAITING或TIMED_WAITING状态,直到它再次被唤醒。

需要注意的是,线程在等待期间会消耗一定的资源,因此应该避免过多的线程等待。另外,线程在等待期间不会占用CPU时间片,因此可以减少CPU的利用率,提高系统的性能。

一、obj.wait()

1.1 作用

wait()是Object里面的方法,Object是所有对象的父类,即所有对象都可以调用wait()方法。wait方法还有可以传入等待时长的,可以让线程等待指定的时间后自动被唤醒。调用wait()会使Java线程进入到WAITING状态,调用wait(long time)会使Java线程进入到TIMED_WAITING状态(WAITING和TIMED_WAITING状态就是阻塞状态)

当一个线程调用一个共享变量的wait()方法时,该线程会阻塞(等待)。直到发生以下几种情况才会恢复执行:

  • 其他线程调用了该共享对象的 notify() 方法或者 notifyAll() 方法(继续往下走)
  • 其他线程调用了该线程的 interrupt() 方法,该线程会 InterruptedException 异常返回

等待线程:假设调用的是obj对象的wait()方法,wait的执行线程,也就是被暂停的线程,就称为对象obj上的等待线程。对象的wait方法可能被不同的线程执行,所以同一个对象可能会有多个等待线程。

1.2 使用前需要持有线程共享对象的锁

在使用wait()、notify()和notifyAll()方法方法前,需要先持有锁。如果调用线程共享对象的wait()、notify()和notifyAll()方法的线程没有事先获取该对象的监视器锁,调用线程会抛出IllegalMonitorStateException 异常。当线程调用wait() 之后,就会释放该对象的监视器锁

使用wait()notify()notifyAll()方法方法前,需要先持有锁:

  • 表象:wait、notify(ALL)方法需要调用 monitor 对象
  • 本质:Java的线程通信实质上是共享内存,而不是直接通信

那么,一个线程如何才能获取一个共享变量的监视器锁?

1、执行synchronized 同步代码块,使用该共享变量作为参数。

synchronized(共享变量) {// TODO
}

2、调用该共享变量的同步方法(synchronized 修饰)

synchronized void sum(int a, int b) {// TODO
}

 如下代码示例,线程A与线程B,在线程A中调用共享变量obj的wait()方法,在线程B中进行唤醒notify()。

/*** Object的Wati()方法的使用*/
@Slf4j
public class WaitTest {public static void main(String[] args) {// 定义一个共享变量Object obj = new Object();// 创建线程AThread threadA = new Thread(new Runnable() {@Overridepublic void run() {log.info("线程" + Thread.currentThread().getName()+"开始执行");try {// 获取共享变量的对象锁synchronized(obj){// 线程A 等待log.info("线程" + Thread.currentThread().getName()+"等待");// 调用wait(),线程A阻塞,并且释放掉获取到的obj的对象锁obj.wait();}} catch (InterruptedException e) {e.printStackTrace();}log.info("线程" + Thread.currentThread().getName()+"执行结束");}},"A");// 创建线程BThread threadB = new Thread(new Runnable() {@Overridepublic void run() {log.info("线程" + Thread.currentThread().getName()+"开始执行");// 获取共享变量锁synchronized (obj){//  线程B 唤醒或者中断  调用obj的唤醒操作或者使A线程中断的操作都可以将正在阻塞的A线程唤醒log.info("线程" + Thread.currentThread().getName()+"唤醒");obj.notify(); // 唤醒操作// threadA.interrupted(); // 中断操作}log.info("线程" + Thread.currentThread().getName()+"执行结束");}},"B");// 启动线程AthreadA.start();try {// 等待200ms,让线程B获取资源,在这200ms期间A就被阻塞了,释放了obj对象锁Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}// 启动线程BthreadB.start();}
}

执行结果:

线程A开始执行

线程B开始执行

线程B执行结束

线程A执行结束

可以看到主程序线程A启动之后,休眠了200ms让出cup执行权,线程B开始执行后调用notify()方法对阻塞线程A进行唤醒。

故:当一个线程调用一个共享变量的wait()方法时,该调用线程会被阻塞挂起,直到发生下面几件事情之一才返回:(1)其他线程调用了该共享对象的notify()或者notifyAll()方法;(2)其他线程调用了该线程的interrupt()方法,该线程抛出InterruptedException异常返回。

1.3 使用技巧

  • wait()方法一般配合while使用
    • 被唤醒后会重新竞争锁,之后从上次wait位置重新运行
    • while 多次判断,防止在wait这段时间内对象被修改

二、obj.notify(All)()

notify()和notifyAll()方法也是Object里面的方法,Object是所有对象的父类,即所有对象都可以调用notify()和notifyAll()方法。但是线程中共享变量在调用这两个方法前,该线程需要获取到这个共享变量的锁才可以,否则会抛出异常。

1.1 notify() 方法

一个线程调用共享对象的 notify() 方法后,会唤醒一个在该共享变量上调用 wait(...) 系列方法后阻塞的线程。

通知线程:调用notify/notifyAll方法时所在的线程叫做通知线程。

1.1.1 调用notify()notifyAll()不会释放线程的锁

当线程调用notify()或notifyAll()方法时,它不会释放掉线程持有的锁。

在Java中,每个对象都有一个相关联的锁,也称为监视器锁。当一个线程需要访问被该锁保护的对象时,它必须先获得该锁的所有权。所以只有获得锁的线程才能调用wait()、notify()和notifyAll()方法。

当线程调用notify()或notifyAll()方法时,它仅仅是唤醒等待在该对象上的一个或多个线程,以便它们可以继续执行。它不会释放线程持有的锁。因此,其他线程仍然无法访问被该锁保护的对象,直到调用notify()或notifyAll()方法的线程释放锁资源。

在多线程编程中,必须小心地管理锁,以避免死锁和竞争条件等问题。通常,为了确保线程安全和避免死锁,必须确保在访问共享资源时只有一个线程持有锁。当然,这也需要合理地使用wait()、notify()和notifyAll()方法来协调线程的执行顺序。

值得注意的是:

  • 一个共享变量上可能会有多个线程在等待,notify()具体唤醒哪个等待的线程是随机的
  • 被唤醒的线程不能马上从wait()方法返回并继续执行,它必须在获取了共享对象的监视器锁后才可以返回,等到唤醒它的线程释放了共享变量上的监视器锁后,被唤醒的线程也不一定会获取到共享对象的监视器锁,这是因为该线程还需要和其他线程一起竞争该锁,只有该线程竞争到了共享变量的监视器锁后才可以继续执行

1.2 notifyAll() 方法

notifyAll() 方法则会唤醒所有在该共享变量上由于调用wait系列方法而被挂起的线程。

1.3 使用技巧

尽量让notify / notifyAll()靠近临界区结束的地方。免得等待线程因为没有获得对象的锁,而又进入等待状态。

三、使用实例

比较经典的就是生产者和消费者的例子。

在生产者消费者模型中,推荐使用notifyAll,因为notify唤醒的线程不确定是生产者或消费者。

public class NotifyWaitDemo {// 共享变量队列的最大容量public static final int MAX_SIZE = 1024;// 共享变量public static Queue queue = new Queue();public static void main(String[] args) {// 生产者Thread producer = new Thread(() -> {// 获取共享变量的锁才能调用wait()方法synchronized (queue) {// 一般wait()都配合着while使用,因为线程唤醒后需要不断地轮循来尝试获取锁while (true) {// 当队列满了之后就挂起当前线程(生产者线程)// 并且,释放通过queue的监视器锁,让消费者对象获取到锁,执行消费逻辑if (queue.size() == MAX_SIZE) {try {// 阻塞生产者线程,并且使当前线程释放掉共享变量的锁queue.wait();} catch (InterruptedException e) {e.printStackTrace();}}// 空闲则生成元素,并且通知消费线程queue.add();// 唤醒消费者来消费,建议用notifyAll(),因为notify()无法确定会唤醒哪一个线程queue.notifyAll();}}});// 消费者Thread consumer = new Thread(() -> {// 需要先获取锁synchronized (queue) {while (true) {// 当队列已经空了之后就挂起当前线程(消费者线程)// 并且,释放通过queue的监视器锁,让生产者对象获取到锁,执行生产逻辑if (queue.size() == 0) {try {// 阻塞消费者线程,并释放共享对象的锁queue.wait();} catch (InterruptedException e) {e.printStackTrace();}}// 空闲则消费元素,并且通知生产线程queue.take();queue.notifyAll();}}});// 先执行生产者线程producer.start();try {// 将当前线程睡眠1000ms,让生产者先将队列生产满,然后wait阻塞起来,并且释放持有的锁。为了后续能执行消费者线程Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// 执行消费者线程consumer.start();}// 共享变量static class Queue {private int size = 0;public int size() {return this.size;}// 生产操作public void add() {// TODOsize++;System.out.println("执行add 操作,current size: " +  size);}// 消费操作public void take() {// TODOsize--;System.out.println("执行take 操作,current size: " +  size);}}
}

 

四、wait()/notify()/notifyAll() 为什么定义在 Object 类中?

由于Thread类继承了Object类,所以Thread也可以调用者三个方法,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法是定义在Object类中。


 相关文章:【并发基础】一篇文章带你彻底搞懂睡眠、阻塞、挂起、终止之间的区别
                  【并发基础】Java中线程的创建和运行以及相关源码分析           
                  【并发基础】线程,进程,协程的详细解释
                 【并发基础】操作系统中线程/进程的生命周期与状态流转以及Java线程的状态流转详解

http://www.dinnco.com/news/23997.html

相关文章:

  • 做电脑网站起什么名字百度统计收费吗
  • java社交网站开发自媒体代运营
  • wordpress的插件安装博客seo怎么做
  • 旅游网站论文摘要h5制作
  • 图片街景位置识别seo优化课程
  • 中国土木工程网新站seo外包
  • 深圳网站建设-中国互联恢复正常百度
  • 公司网站建设全微博推广方案
  • 网站开发需求单百度极速版免费下载
  • 九江市城市建设投资有限公司seo论坛站长交流
  • 重庆奉节网站建设公司哪里有惠州网络营销
  • 海西高端网站建设公司上海有名网站建站开发公司
  • 建设网站的报告北京学电脑的培训机构
  • 网站建设企业建站广告网站建设网站排名优化
  • 帝国网站系统做专题网络公司网站建设
  • 做电商网站必需知道qc湖南网站seo
  • 单位有公网ip怎么做网站seminar怎么读
  • FPGA毕业设计代做网站网络营销策划案
  • 效果好的网站建专业网站建设
  • 成都免费建站模板长沙建设网站制作
  • 中国菲律宾概念股网站seo收录
  • 苏州网站开发外包公司湖南做网站的公司
  • 怎样创建网站教程bt磁力猫
  • 湖南建设c证查询网站抚顺网站建设
  • 外贸网站如何做推广网络营销包括的主要内容有
  • wordpress+培训模板西安关键词seo
  • 山西建设公司网站排名前50名免费的网站
  • 网页制作工具的是seo是啥软件
  • 服装怎么做网站推广镇江网站制作公司
  • 天津和平做网站东营网站建设制作