Java线程——线程之间的几点重要说明
在Java中,可以通过配合调用Object对象的wait()方法和notify()方法或notifyAll()方法来实现线程间的通信。在线程中调用wait()方法,将阻塞等待其他线程的通知(其他线程调用notify()方法或notifyAll()方法),在线程中调用notify()方法或notifyAll()方法,将通知其他线程从wait()方法处返回。
Object是所有类的超类,它有5个方法组成了等待/通知机制的核心:notify()、notifyAll()、wait()、wait(long)和wait(long,int)。在Java中,所有的类都从Object继承而来,因此,所有的类都拥有这些共有方法可供使用。而且,由于他们都被声明为final,因此在子类中不能覆写任何一个方法。
这里详细说明一下各个方法在使用中需要注意的几点:
1、wait()
public final void wait() throws InterruptedException,IllegalMonitorStateException
该方法用来将当前线程置入休眠状态,直到接到通知或被中断为止。在调用wait()之前,线程必须要获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法。进入wait()方法后,当前线程释放锁。在从wait()返回前,线程与其他线程竞争重新获得锁。如果调用wait()时,没有持有适当的锁,则抛出IllegalMonitorStateException,它是RuntimeException的一个子类,因此,不需要try-catch结构。
2、notify()
public final native void notify() throws IllegalMonitorStateException
该方法也要在同步方法或同步块中调用,即在调用前,线程也必须要获得该对象的对象级别锁,的如果调用notify()时没有持有适当的锁,也会抛出IllegalMonitorStateException。
该方法用来通知那些可能等待该对象的对象锁的其他线程。如果有多个线程等待,则线程规划器任意挑选出其中一个wait()状态的线程来发出通知,并使它等待获取该对象的对象锁(notify后,当前线程不会马上释放该对象锁,wait所在的线程并不能马上获取该对象锁,要等到程序退出synchronized代码块后,当前线程才会释放锁,wait所在的线程也才可以获取该对象锁),但不惊动其他同样在等待被该对象notify的线程们。当第一个获得了该对象锁的wait线程运行完毕以后,它会释放掉该对象锁,此时如果该对象没有再次使用notify语句,则即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,会继续阻塞在wait状态,直到这个对象发出一个notify或notifyAll。这里需要注意:它们等待的是被notify或notifyAll,而不是锁。这与下面的notifyAll()方法执行后的情况不同。
3、notifyAll()
public final native void notifyAll() throws IllegalMonitorStateException
该方法与notify()方法的工作方式相同,重要的一点差异是:
notifyAll使所有原来在该对象上wait的线程统统退出wait的状态(即全部被唤醒,不再等待notify或notifyAll,但由于此时还没有获取到该对象锁,因此还不能继续往下执行),变成等待获取该对象上的锁,一旦该对象锁被释放(notifyAll线程退出调用了notifyAll的synchronized代码块的时候),他们就会去竞争。如果其中一个线程获得了该对象锁,它就会继续往下执行,在它退出synchronized代码块,释放锁后,其他的已经被唤醒的线程将会继续竞争获取该锁,一直进行下去,直到所有被唤醒的线程都执行完毕。
4、wait(long)和wait(long,int)
显然,这两个方法是设置等待超时时间的,后者在超值时间上加上ns,精度也难以达到,因此,该方法很少使用。对于前者,如果在等待线程接到通知或被中断之前,已经超过了指定的毫秒数,则它通过竞争重新获得锁,并从wait(long)返回。另外,需要知道,如果设置了超时时间,当wait()返回时,我们不能确定它是因为接到了通知还是因为超时而返回的,因为wait()方法不会返回任何相关的信息。但一般可以通过设置标志位来判断,在notify之前改变标志位的值,在wait()方法后读取该标志位的值来判断,当然为了保证notify不被遗漏,我们还需要另外一个标志位来循环判断是否调用wait()方法。
深入理解:
如果线程调用了对象的wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。
当有线程调用了对象的notifyAll()方法(唤醒所有wait线程)或notify()方法(只随机唤醒一个wait线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。
优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了synchronized代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。
————————————————
版权声明:本文为CSDN博主「兰亭风雨」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ns_code/article/details/17225469
Java线程——线程之间的几点重要说明的更多相关文章
- Java多线程——线程之间的协作
Java多线程——线程之间的协作 摘要:本文主要学习多线程之间是如何协作的,以及如何使用wait()方法与notify()/notifyAll()方法. 部分内容来自以下博客: https://www ...
- Java多线程——线程之间的同步
Java多线程——线程之间的同步 摘要:本文主要学习多线程之间是如何同步的,如何使用volatile关键字,如何使用synchronized修饰的同步代码块和同步方法解决线程安全问题. 部分内容来自以 ...
- Java线程——线程之间的数据共享
在 Java 传统线程机制中的共享数据方式,大致可以简单分两种情况: ➢ 多个线程行为一致,共同操作一个数据源.也就是每个线程执行的代码相同,可以使用同一个 Runnable 对象,这个 Runn ...
- java之线程
java之线程 一:线程: 线程是什么呢?线程,有时被称为轻量级进程是程序执行流的最小单元.一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成.另外,线程是进程中的一个实体,是被系统 ...
- Java的线程安全
线程安全 我们这里讨论的线程安全,就限定于多个线程之间存在共享数据访问这个前提,因为如果一段代码根本不会与其他线程共享数据,那么从线程安全的角度来看,程序是串行执行还是多线程执行对它来说是完全没有区别 ...
- 深入理解Java之线程池
原作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本文归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则 ...
- java 多线程—— 线程等待与唤醒
java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...
- Java的线程模型
并发不一定要依赖多线程(如PHP中很常见的多进程并发),但是在Java里面谈论并发,大多数都与线程脱不开关系. 线程是比进程更轻量级的调度执行单位,线程的引入,可以把一个进程的资源分配和执行调度分开, ...
- Java Thread线程控制
一.线程和进程 进程是处于运行中的程序,具有一定的独立能力,进程是系统进行资源分配和调度的一个独立单位. 进程特征: A.独立性:进程是系统中独立存在的实体,可以拥有自己独立的资源,每个进程都拥有自己 ...
- Java并发——线程安全、线程同步、线程通信
线程安全 进程间"共享"对象 多个“写”线程同时访问对象. 例:Timer实例的num成员,即add()方法是用的次数.即Timer实例是资源对象. class TestSync ...
随机推荐
- 八十七、SAP中ALV事件之一,事件的声明
一.我们双击"REUSE_ALV_GRID_DISPLAY",来到SE37界面, 二,来到这儿,点击SLIS_T_EVENT, 三.可以看到SLIS_T_EVENT的定义,有一个n ...
- python_re正则表达
re模块就本质而言,正则表达式(或RE)是一种小型的.高度专业化的编程语言,(在python中)它内嵌在Python中,并通过re模块实现,正则表达式模块被编译成一系列的字节码,然后由用C编写的匹配引 ...
- ofo身陷被收购、破产传闻,中国的共享单车还能活下去吗?
大潮退去,终将现出谁在"裸泳".尤其是那些看似火爆却迅速陨落的新事物,总是避免不了让人发出"伤仲永"的感慨.这其中,共享经济就是很典型的案例.共享睡眠舱.共享马 ...
- CSS - 导入Google Web 字体
@import url('http://fonts.googleapis.com/css?family=Poppins:100,200,300,400,500,600,700,800,900'); b ...
- oracle批量修改字段长度
alter table 表名 modify (字段名1 字段类型1(长度1),字段名2 字段类型2(长度2)) alter table 表名 modify column_name varchar2(3 ...
- EBGP的多跳与验证命令
EBGP的多跳与验证命令: ①:neighbor router-id ebgp-multihop “int”——设置多跳. ②:neighbor router-id password “str”——设 ...
- swift中使用UIColllectionView实现横向轮播的一般方法
// // HomeLiveRankCell.swift // YYSwiftProject // // Created by Domo on 2018/7/28. // Copyright ...
- oracle数据删除恢复
insert into 表名 select * from 表名 as of timestamp to_Date('2017-07-20 10:00:00', 'yyyy-mm-dd hh24:mi:s ...
- VUE学习(一)——使用npm安装项目
npm是node.js自带的功能 Node.js 安装配置 本章节我们将向大家介绍在 Windows 和 Linux 上安装 Node.js 的方法. 本安装教程以 Node.js v4.4.3 LT ...
- (5)opencv的基础操作和矩阵的掩模操作
不懂的,可以简单,看看这个网址:https://blog.csdn.net/xiongwen_li/article/details/78503491 图片放到了桌面,所以,图片的路径就是桌面了,剩余的 ...