瘟疫青年

线程阻塞和中断的四种方式

Word count: 827Reading time: 3 min
2018/03/13 Share

1、线程阻塞

一个线程进入阻塞状态可能的原因:

通过调用sleep(millseconds)使任务进入休眠状态;

1
2
3
4
5
6
7
8
9
10
11
12
class Demo1 implements Runnable throws InterruptedException{
public void run(){
Thread.sleep(1000);
}
}
②通过调用wait()使线程挂起,直到线程获取notify()/notifyAll()消息,(或者在Java SE5中java.util.concurrent类库中等价的signal()/signalAll()消息),线程才会进入就绪状态;
class Demo2 implements Runnable{
public void run(){
Thread.await();
Thread.notify();
}
}

任务在等待某个输入 / 输出流的完成;

1
2
3
4
5
6
class Demo3 implements Runnable throws InterruptedException{
private InputStream in;
public void run(){
in.read();
}
}

任务试图在某个对象上调用其同步控制方法,但是对象锁不可用,因为另一个任务已经获取了该锁;

1
2
3
4
5
6
7
class Demo4 implements Runnable{
public synchronized void method1(){ }
public synchronized void method2(){ }
public void run(){
method1();
}
}

2、线程中断的方法

Thread类包含interrupt()方法,用于终止阻塞任务;

1)中断①②类线程休眠,挂起阻塞的方法

1.直接使用Thread.interrupt();
1
2
3
4
main(){
Thread t = new Thread(new Demo1());
t.interrupt();
}
2.使用Executor线程池,中断线程池中的所有线程;
1
2
3
4
5
6
main(){
ExecutorService exec = Executors.newCachedThreadPool();
for(int i=0;i<5;i++)
exec.execute(new Demo1())
exec.shutdownNow();
}
3.使用Executor线程池,中断线程池中单个阻塞的线程;
1
2
3
4
5
main(){
ExecutorService exec = Executors.newCachedThreadPool();
Futrue<?> f = exec.submit(new Demo1());
f.interrupt();
}

//中断后的清除代码放置在InterruptedException异常的catch捕获的代码块中

2)中断③类I/O阻塞的方法

使用Thread.iterrupt方法无法中断I/O阻塞,这对于基于Web的程序是很不利的;

有一种解决方法:关闭任务在其上发生阻塞的底层资源;
1
2
3
4
5
6
7
8
9
10
11
12
main(){
ExecutorService exec = Executors.newCachedThreadPool();
ServerSocket server = new ServerSocket(8080);
InputStream socketInput = new Socket("localhost",8080)
exec.execute(socketInput);
exec.execute(Sytsem.in);
//exec.shutdownNow(); 无法中断2个线程;

socketInput.close();
in.close();
exec.shutdownNow();
}
java.nio类库提供了更加人性化的I/O中断,被阻塞的nio通道会自动地响应中断;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Demo impelenets Runnable{
private final SocketChannel sc;
public Demo(SocketChannel sc){ this.sc = sc;}
public void run(){
try{
sc.read(ByteBuffer.allocate(1));
}catch(CloseByInteruptedException e1){
}catch(AsyncronousCloseException e2){
}catch(IOException e3){
}
}
}
public Test {
public static void main(){
ExecutorService exec = Executors.newCachedThreadPool();
ServerSocket server = new ServerSocket(8080);
InetSocketAddress isa = new InetSocketAddress("localhost",8080);
SocketChannel sc1 = new SocketChannel.open(isa);
SocketChannel sc2 = new SocketChannel.open(isa);

exec.execute(new Demo(sc1));
Future<?> f = exec.submit(new Demo(sc2));
f.cancel(true); //可以终止sc1通道所在的线程;
exec.shutdownNow(); //可以终止exec线程池内所有的线程;
sc1.close();
sc2.close();
}
}

3)中断④类被互斥阻塞的线程

使用Thread.iterrupt方法无法中断互斥类线程,

解决方式1:可以使用ReentrantLock显式加锁,在JavaSE5中引入的新特性,ReentrantLock上阻塞的任务可以被中断;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Task imlements Runnable{
private Lock lock = new ReentrantLock();
public void run(){
lock.lock();
try{
while(true)
}catch(InterruptedExcpetion e){
System.out.println("The Task is interrupted!");
}finally{
lock.unlock();
}
}
public void main(){
Thread t = new Thread(new Task());
t.start();
t.interrupt();
}
}
解决方式2:使用一个while(!Thread.interrupted())包裹同步的代码块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Task impelments Runnable{
private synchronized void method1(){ }
public void run(){
try{
whlie(!Thread.interrupted())
method1();
}catch(InteruptedException e){
}
}
public static void main(){
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new Task());
exec.shutdownNow(); //线程被打断
/*或 Thread t = new Thread(new Task());
t.start();
t.interrupt(); */
}
}
CATALOG
  1. 1. 1、线程阻塞
    1. 1.0.1. 通过调用sleep(millseconds)使任务进入休眠状态;
    2. 1.0.2. 任务在等待某个输入 / 输出流的完成;
    3. 1.0.3. 任务试图在某个对象上调用其同步控制方法,但是对象锁不可用,因为另一个任务已经获取了该锁;
  • 2. 2、线程中断的方法
    1. 2.0.1. 1)中断①②类线程休眠,挂起阻塞的方法
      1. 2.0.1.1. 1.直接使用Thread.interrupt();
      2. 2.0.1.2. 2.使用Executor线程池,中断线程池中的所有线程;
      3. 2.0.1.3. 3.使用Executor线程池,中断线程池中单个阻塞的线程;
    2. 2.0.2. 2)中断③类I/O阻塞的方法
      1. 2.0.2.1. 有一种解决方法:关闭任务在其上发生阻塞的底层资源;
      2. 2.0.2.2. java.nio类库提供了更加人性化的I/O中断,被阻塞的nio通道会自动地响应中断;
    3. 2.0.3. 3)中断④类被互斥阻塞的线程
      1. 2.0.3.1. 解决方式1:可以使用ReentrantLock显式加锁,在JavaSE5中引入的新特性,ReentrantLock上阻塞的任务可以被中断;
      2. 2.0.3.2. 解决方式2:使用一个while(!Thread.interrupted())包裹同步的代码块