java并发编程(一)线程状态 & 线程中断 & 线程间的协作
参考文章:
Java线程的5种状态及切换:http://blog.csdn.net/pange1991/article/details/53860651
线程的5种状态:
1. 新建(NEW):新创建了一个线程对象。
2. 可运行(RUNNABLE):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权
3. 运行(RUNNING):可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。
4. 阻塞(BLOCKED):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种:
(1). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
(2). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
(3). 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。
5. 死亡(DEAD):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生
线程的状态切换图

/**
* 计算输出其他线程锁计算的数据
*
*/
public class ThreadA {
public static void main(String[] args) throws InterruptedException{
ThreadB b = new ThreadB();
//启动计算线程
b.start();
//线程A拥有b对象上的锁。线程为了调用wait()或notify()方法,该线程必须是那个对象锁的拥有者
synchronized (b) {
System.out.println("等待对象b完成计算。。。");
//当前线程A等待
b.wait();
System.out.println("b对象计算的总和是:" + b.total);
}
}
} /**
* 计算1+2+3 ... +100的和
*
*/
class ThreadB extends Thread {
int total; public void run() {
synchronized (this) {
for (int i = 0; i < 101; i++) {
total += i;
}
//(完成计算了)唤醒在此对象监视器上等待的单个线程,在本例中线程A被唤醒
notify();
System.out.println("计算完成");
}
}
}
为什么进入wait和notify的时候要加synchronized锁?
通俗的解释: 只有两个线程抢一个资源的时候才存在两个线程同一时刻只能有一个线程得到资源,锁就是为了使两个线程抢同一个资源,如果没有锁,意思是两个线程不存在抢资源情况,那两个线程凭什么你等我 我等你,早就一起跑了
wait方法执行后未退出同步块,其他线程如何进入同步块?
执行wait,会释放锁
为什么wait方法可能抛出InterruptedException异常?
为了可以中断线程?
notify执行之后立马唤醒线程吗?
不会。默认是先退出同步块才真正的唤醒等待线程
中断线程
每一个线程都有一个boolean类型标志,用来表明当前线程是否请求中断,当一个线程调用interrupt() 方法时,线程的中断标志将被设置为true。
我们可以通过调用Thread.currentThread().isInterrupted()或者Thread.interrupted()来检测线程的中断标志是否被置位。这两个方法的区别是
Thread.currentThread().isInterrupted()是线程对象的方法,调用它后不清除线程中断标志位;而Thread.interrupted()是一个静态方法,调用它会清除
线程中断标志位。
所以说调用线程的interrupt() 方法不会中断一个正在运行的线程,这个机制只是设置了一个线程中断标志位,如果在程序中你不检测线程中断标志位,那么即使
设置了中断标志位为true,线程也一样照常运行。
中断线程分为三种情况:
中断非阻塞线程
中断非阻塞线程通常有两种方式:
(1)采用线程共享变量
这种方式比较简单可行,需要注意的一点是共享变量必须设置为volatile,这样才能保证修改后其他线程立即可见
public class InterruptThreadTest extends Thread{
// 设置线程共享变量
volatile boolean isStop = false;
public void run() {
while(!isStop) {
long beginTime = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName() + "is running");
// 当前线程每隔一秒钟检测一次线程共享变量是否得到通知
while (System.currentTimeMillis() - beginTime < 1000) {}
}
if (isStop) {
System.out.println(Thread.currentThread().getName() + "is interrupted");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
InterruptThreadTest itt = new InterruptThreadTest();
itt.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 线程共享变量设置为true
itt.isStop = true;
}
}
(2) 采用中断机制
public class InterruptThreadTest2 extends Thread{
public void run() {
// 这里调用的是非清除中断标志位的isInterrupted方法
while(!Thread.currentThread().isInterrupted()) {
long beginTime = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName() + "is running");
// 当前线程每隔一秒钟检测线程中断标志位是否被置位
while (System.currentTimeMillis() - beginTime < 1000) {}
}
if (Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + "is interrupted");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
InterruptThreadTest2 itt = new InterruptThreadTest2();
itt.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 设置线程的中断标志位
itt.interrupt();
}
}
中断阻塞线程
当线程调用Thread.sleep()、Thread.join()、object.wait()再或者调用阻塞的i/o操作方法时,都会使得当前线程进入阻塞状态。那么此时如果在线程处于阻塞状态是调用
interrupt() 方法设置线程中断标志位时, 处于阻塞状态的线程会抛出一个异常,并且会清除线程中断标志位(设置为false)。这样一来线程就能退出
阻塞状态。当然抛出异常的方法就是造成线程处于阻塞状态的Thread.sleep()、Thread.join()、object.wait()这些方法。
public class InterruptThreadTest3 extends Thread{
public void run() {
// 这里调用的是非清除中断标志位的isInterrupted方法
while(!Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + " is running");
try {
System.out.println(Thread.currentThread().getName() + " Thread.sleep begin");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " Thread.sleep end");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
//由于调用sleep()方法清除状态标志位 所以这里需要再次重置中断标志位 否则线程会继续运行下去
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
if (Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + "is interrupted");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
InterruptThreadTest3 itt = new InterruptThreadTest3();
itt.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 设置线程的中断标志位
itt.interrupt();
}
不可中断线程
有一种情况是线程不能被中断的,就是调用synchronized关键字和reentrantLock.lock()获取锁的过程
线程间的协作
object类的方法wait/notify/notifyAll
wait():
作用于持有该对象锁的当前线程,使线程从运行态,进入到等待阻塞状态,释放对象锁,释放cpu控制权,调用该方法的前提是获得对象锁
notify()/notifyAll:
作用于持有该对象锁的线程(其中一个/全部),使线程从等待阻塞状态,进入到锁池阻塞状态,调用该方法的前提是获得对象锁
Thread类的方法sleep/yield/join
sleep():
作用于当前线程,使其进入阻塞状态,释放cpu,时间到了即可唤醒,唤醒后进入就绪状态
yield():
作用于当前线程,使其进入就绪状态,暂时释放cpu,下个抢到cpu的还可能是该线程本身。(意图是让相同优先级的其他线程有机会拿到cpu)
join():
作用于当前线程,使其进入阻塞状态,释放cpu,直到线程t(t.join())执行完毕。(源码是通过wait方法实现的)
wait/notify/notifyAll方法的作用是实现线程间的协作,那为什么这三个方法不是位于Thread类中,而是位于Object类中?
位于Object中,也就相当于所有类都包含这三个方法(因为Java中所有的类都继承自Object类)
要回答这个问题,还是得回过来看wait方法的实现原理,大家需要明白的是,wait等待的到底是什么东西?wait等待的其实是对象monitor,由于Java中的每一个对象都有一个内置的monitor对象,自然所有的类都理应有wait/notify方法。
java并发编程(一)线程状态 & 线程中断 & 线程间的协作的更多相关文章
- Java并发编程(二)如何保证线程同时/交替执行
第一篇文章中,我用如何保证线程顺序执行的例子作为Java并发系列的开胃菜.本篇我们依然不会有源码分析,而是用另外两个多线程的例子来引出Java.util.concurrent中的几个并发工具的用法. ...
- Java并发编程原理与实战四:线程如何中断
如果你使用过杀毒软件,可能会发现全盘杀毒太耗时间了,这时你如果点击取消杀毒按钮,那么此时你正在中断一个运行的线程. java为我们提供了一种调用interrupt()方法来请求终止线程的方法,下面我们 ...
- java并发编程JUC第九篇:CountDownLatch线程同步
在之前的文章中已经为大家介绍了java并发编程的工具:BlockingQueue接口.ArrayBlockingQueue.DelayQueue.LinkedBlockingQueue.Priorit ...
- Java并发编程中的阻塞和中断
>>线程的状态转换 线程的状态转换是线程控制的基础,下面这张图片非常直观的展示了线程的状态转换: 线程间的状态转换: 1. 新建(new):新创建了一个线程对象.2. 可运行(runnab ...
- Java并发编程的艺术(五)——线程和线程的状态
线程 什么是线程 操作系统调度的最小单元就是线程,也叫轻量级进程. 为什么要使用多线程 多线程程序能够更有效率地利用多处理器核心. 用户响应时间更快. 方便程序员将程序模型映射到Java提供的多线程编 ...
- Java并发编程原理与实战七:线程带来的风险
在并发中有两种方式,一是多进程,二是多线程,但是线程相比进程花销更小且能共享资源.但使用多线程同时会带来相应的风险,本文将展开讨论. 一.引言 多线程将会带来几个问题: 1.安全性问题 线程安全性可能 ...
- Java并发编程(三)什么是线程池
什么是线程池 学习编程的小伙伴们会经常听到“线程池”.“连接池”这类的词语,可是到底“池”是什么意思呢?我讲个故事大家就理解了:在很久很久以前有一家银行,一年之中只有一个客户来办理业务,随着时间的推移 ...
- Java 并发编程中的 Executor 框架与线程池
Java 5 开始引入 Conccurent 软件包,提供完备的并发能力,对线程池有了更好的支持.其中,Executor 框架是最值得称道的. Executor框架是指java 5中引入的一系列并发库 ...
- java并发编程(四)守护进程 线程阻塞的四种情况
转载请注明出处:http://blog.csdn.net/ns_code/article/details/17099981 守护线程 Java中有两类线程:User Thread(用户线程).Da ...
- 【Java并发编程】14、Thread,线程说明
线程的状态:New.Runnable.Blocked.Waiting.Timed waiting.Terminated 1. RUNNABLE,对应"就绪"和"运行&qu ...
随机推荐
- Java自学-接口与继承 super
Java的super关键字 步骤 1 : 准备一个显式提供无参构造方法的父类 准备显式提供无参构造方法的父类 在实例化Hero对象的时候,其构造方法会打印 "Hero的构造方法 " ...
- Java自学-类和对象 类方法
Java的类方法和对象方法 类方法: 又叫做静态方法 对象方法: 又叫实例方法,非静态方法 访问一个对象方法,必须建立在有一个对象的前提的基础上 访问类方法,不需要对象的存在,直接就访问 步骤 1 : ...
- javaScript 一些小技巧
日历 创建过去七天的数组,如果将代码中的减号换成加号,你将得到未来7天的数组集合 // 创建过去七天的数组 [...Array(7).keys()].map(days => new Date(D ...
- css, js 项目练习之网页换肤
首先,该练习参考自:https://www.jianshu.com/p/2961d9c317a3 我就直接上代码了(颜色可以自己调). HTML: <nav> <li>< ...
- YUV视频格式详解(翻译自微软文档)
原文: https://docs.microsoft.com/en-us/previous-versions/aa904813(v=vs.80) YUV视频格式详解(翻译自微软文档)https://b ...
- Redhat6.5安装oracle11g
Redhat6.5安装oracle11g 一. 安装环境 linux服务器:Redhat 6.5 64位 oracle版本:oracle11gR2 远程windows服务器:已安装Xmanage ...
- Idea 热部署插件JRebel 安装与环境配置-上海尚学堂Java培训
在企业日常项目开发中,如果我们需要调试一个Java Web项目,就需要先将项目编译之后,放入Web容器或借助Maven web 插件来运行,如果对Java源代码进行修改,那么必须重新编译并重启Web容 ...
- Python之数据分析
什么是数据分析? 运用不同行业中,专门从事行业数据搜集.整理.分析,并依据数据做出行业研究.评估和预测的专业人员. 熟悉行业知识.公司业务及流程,最好有自己独到的见解,若脱离行业认知和公司业务背景,分 ...
- 从SQLAlchemy的“缓存”问题说起
https://www.jianshu.com/p/c0a8275cce99 0.4792017.11.22 00:07:04字数 1631阅读 6493 问题描述 最近在排查一个问题,为了方便说明, ...
- Android Studio--Logcat
他只是坐在那里,嘴里说:“做这个!做那个!当然,什么都不会发生,光说不做是没有用的” --哈里·杜鲁门“论<总统的权利>” 移动端的技术演进愈演愈烈,原生突破口已被打破. flutter. ...