一、线程的状态
1.线程状态:
新建:当线程被创建的时候,会短暂处于这种状态。此时已经分配了系统资源,并执行了初始化。
就绪:在这种状态下,只要调度器把cpu时间片分给线程就可以执行
阻塞;线程可以运行,但摸个条件阻止它的运行。此时调度器将忽略线程,直到线程进入就绪状态才又可能执行操作
死亡:处于死亡或终止状态的线程将不再是可以调度的,它的任务以结束,或不再是可运行的
二、线程进入阻塞的原因:
1.休眠时阻塞
class SleepBlock implements Runnable{
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
System.out.println("InterruptedException");
}
System.out.println("Exiting SleepBlock run()");
}
}
2.等待阻塞
通过wait()将线程挂起,直到线程得到了notify()或notifyAll()消息(或者是concurrent类库中等价的signal或signalAll),线程才会进入就绪状态。
public synchronized void waxed() {//打蜡
waxOn = true;
notifyAll();
}
public synchronized void waitForWaxing() throws InterruptedException {
while (waxOn == false)
wait();
}
3.I/O阻塞
等待某个输出/输入完成
//3.输入输出导致堵塞
class IOBlock implements Runnable{
private InputStream in;
IOBlock(InputStream in){
this.in=in;
}
@Override
public void run() {
try {
System.out.println("等待输入");
in.read();
} catch (IOException e) {
if (Thread.currentThread().isInterrupted()){
System.out.println("Interrupted from block I/O");
}else{
throw new RuntimeException(e);
}
}
System.out.println("Exiting I/OBlock run()");
}
}
4.互斥时阻塞
任务试图在某个对象上调用其同步控制方法,但是对象锁不可用,因为另一个任务已经获取了这个锁。
class SynchroizedBlocked implements Runnable{
public synchronized void f(){
while (true){
Thread.yiled();//程序将再次阻塞
}
public void run(){
System.out.println("等待任务执行");
f();//在f()循环的时候阻塞
System.out.println("任务执行结束");
}
}
扩展:
1.可以通过jps命令来查看阻塞的程序,通过jstack来查程序中阻塞的线程
2.也可以通过jconsole来调用java后台监视系统:
三、 中断:
1.中断线程的方式:
Thread类中包含interrupt()方法,可以终止被阻塞的任务,被终止的任务将将抛出InterruptedException异常
在Executor中调用showdowNow(),它将发送一个Interrupt()调用给它启动的所以线程。从而引发打断异常
cancel()方法:是一种用来中断由Exceutor启动的单个线程的方式。Exceutor中的submit来创建线程,通过返回一个泛型的Future<?>,来调用cancel方法,来终止某个特定的任务。如果将true传统给该方法,那他就会拥有在该线程上调用interrupt()已停止这个线程的权限。
2.终止因休眠阻塞的线程:
/1.sleep导致阻塞
class SleepBlock implements Runnable{
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
System.out.println("InterruptedException");
}
System.out.println("Exiting SleepBlock run()");
}
}
public class Interrupting {
private static ExecutorService executor = Executors.newCachedThreadPool();
static void test(Runnable r) throws InterruptedException {
Future<?> f = executor.submit(r);
TimeUnit.MILLISECONDS.sleep(100);
System.out.println("Interrupting "+r.getClass().getName());
f.cancel(true);//中断任务
System.out.println("Interrupting sent to"+r.getClass().getName());
}
//jstack
public static void main(String[] args) throws InterruptedException {
test(new SleepBlock());//定时阻塞
TimeUnit.SECONDS.sleep(50);
System.out.println("系统关闭");
System.exit(0);
}
}
3.中断I/O导致的阻塞(io阻塞不能中断)
class IOBlock implements Runnable{
private InputStream in;
IOBlock(InputStream in){
this.in=in;
}
@Override
public void run() {
try {
System.out.println("等待输入");
in.read();
} catch (IOException e) {
if (Thread.currentThread().isInterrupted()){
System.out.println("Interrupted from block I/O");
}else{
throw new RuntimeException(e);
}
}
System.out.println("Exiting I/OBlock run()");
}
}
public class Interrupting {
private static ExecutorService executor = Executors.newCachedThreadPool();
static void test(Runnable r) throws InterruptedException {
Future<?> f = executor.submit(r);
TimeUnit.MILLISECONDS.sleep(100);
System.out.println("Interrupting "+r.getClass().getName());
f.cancel(true);//中断任务
System.out.println("Interrupting sent to"+r.getClass().getName());
}
public static void main(String[] args) throws InterruptedException {
test(new IOBlock(System.in));//输入输出阻塞
TimeUnit.SECONDS.sleep(10);
System.out.println("系统关闭");
System.exit(0);
}
}
我们发现,通过调用Future类中的cancel方法并不能中断IO,所以对于IO的阻塞去关闭底层的数据源:
public class CloseResource {
public static void main(String[] args) throws IOException, InterruptedException {
ExecutorService service = Executors.newCachedThreadPool();
ServerSocket serverSocket = new ServerSocket(8080);
InputStream inputStream = new Socket("localhost",8080).getInputStream();
service.execute(new IOBlock(inputStream));
service.execute(new IOBlock(System.in));
TimeUnit.MILLISECONDS.sleep(200);
System.out.println("Shutting down all thread");
service.shutdownNow();只能引发打断异常
//关闭底层资源
TimeUnit.SECONDS.sleep(1);
System.out.println("Cloas"+inputStream.getClass().getName());
inputStream.close();//只能中断Socket
TimeUnit.SECONDS.sleep(1);
System.out.println("Cloas"+System.in.getClass().getName());
System.in.close();//1.8以后不中断InputStream
}
}
4.中断由SynchroizedBlocked锁导致的阻塞:
示列一:
class SynchroizedBlocked implements Runnable{
public synchronized void f(){
while (true)
Thread.yield();//有一个任务已经获得锁则其他任务进不来,该任务则一直在运行,从而造成阻塞
}
public SynchroizedBlocked(){
new Thread(){
@Override
public void run() {
f();
System.out.println("其他业务逻辑代码");
}
}.start();
}
@Override
public void run() {
System.out.println("Trying to call f()");
f();
System.out.println("Exiting synchoizedBlock.run()");
}
}
public class Interrupting {
private static ExecutorService executor = Executors.newCachedThreadPool();
static void test(Runnable r) throws InterruptedException {
Future<?> f = executor.submit(r);
TimeUnit.MILLISECONDS.sleep(100);
System.out.println("Interrupting "+r.getClass().getName());
f.cancel(true);//中断任务
System.out.println("Interrupting sent to"+r.getClass().getName());
}
public static void main(String[] args) throws InterruptedException {
TimeUnit.SECONDS.sleep(10);
System.out.println("系统关闭");
System.exit(0);
}
}
示列二:
当被锁的任务为死循环时:
class SynchroizedBlocked implements Runnable{
private volatile AtomicInteger count=new AtomicInteger();
private volatile boolean falg=false;
public synchronized void f(){
while (!falg){
int i = count.addAndGet(2);
if (i==50)
falg=true;
}
}
当循环次数超过设定的次数时候,原子性操作的Boolean将变为true从而跳出循环。
注意:我们去不能去终止正在试图获取Synchronized锁或者试图执行I/O操作的线程。
示列三:一个同一个互斥被同一个任务多次获得
package com.package21.multilock;
public class MultiLock {
public synchronized void f1(int count){
if (count-->0){
System.out.println("f1() and f2() "+count);
f2(count);
}
}
public synchronized void f2(int count){
if (count-->0){
System.out.println("f2() and f1() "+count);
f1(count);
}
}
public static void main(String[] args) {
MultiLock multiLock = new MultiLock();
new Thread (){
@Override
public void run() {
multiLock.f1(10);
}
}.start();
}
}
5.ReentranLock中断阻塞:
无论何时,只要任务不可中断式被阻塞,都有潜在锁住程序的可能,而ReentranLock上阻塞的任务具备可以被中断的能力。
public class BlockedMutex {
private Lock lock = new ReentrantLock();
public BlockedMutex() {
lock.lock();//加锁
}
public void f() {
try {
lock.lockInterruptibly();//如果当前线程未被中断则获得锁,如果当前线程被中断则出现异常。
System.out.println("lock f()");
} catch (InterruptedException e) {
System.out.println("Interrupted form lpck in f()");
}
}
}
class BlockedMutex_Two implements Runnable {
BlockedMutex blockedMutex = new BlockedMutex();
@Override
public void run() {
System.out.println("waiting for f() in BlockedMutex");
blockedMutex.f();
System.out.println("Broken out of blocked call");
}
}
class Interrupting2 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new BlockedMutex_Two());
thread.start();
TimeUnit.SECONDS.sleep(1);
System.out.println("Issuing t.interrupt()");
thread.interrupt();
}
}
四、检查中断:
1. 通过interrupted()方法来检查线程是否中断
//标识类
class NeedsCleanup {
private final int id;
public NeedsCleanup(int ident) {
id = ident;
System.out.println("NeedsCleanup " + id);
}
public void cleanup() {
System.out.println("Cleaning up " + id);
}
}
class Blocked3 implements Runnable {
private volatile double d = 0.0;
public void run() {
try {
while (!Thread.interrupted()) {//检查线程是否中断
NeedsCleanup n1 = new NeedsCleanup(1);
try {
System.out.println("Sleeping");
TimeUnit.SECONDS.sleep(2);
NeedsCleanup n2 = new NeedsCleanup(2);
try {
System.out.println("Calculating");
for (int i = 1; i < 2500000; i++)
d = d + (Math.PI + Math.E) / d;
System.out.println("Finished time-consuming operation");
} finally {
n2.cleanup();
}
}
finally {
n1.cleanup();
}
}
System.out.println("Exiting via while() test");
} catch (InterruptedException e) {
System.out.println("Exiting via InterruptedException");
}
}
}
public class InterruptingIdiom {
public static void main(String[] args) throws Exception {
Thread t = new Thread(new Blocked3());
t.start();
TimeUnit.MILLISECONDS.sleep(2010);
t.interrupt();
}
}
注意:所有可能中断需要被清理的对象创建操作的后面。都必须紧跟Try-finally子句,从而使无论run循环如何退出,清理都会发生。