import java.io.IOException;
import java.io.InputStream;
import java.sql.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* 以下的每个任务都表示了一种不同类型的阻塞。
* SleepBlock是可中断的阻塞示例
* 在run()中调用了sleep()
*
* @create @author Henry @date 2016-12-13
*/
class SleepBlocked implements Runnable {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
System.out.println("InterruptedException");
}
System.out.println("Exiting SleepBlocked.run()");
}
}
/**
* IOBlocked 是不可中断的阻塞示例。
* 在I/O块上等待是不可中断的。
* 在此类中调用了read()。Thread类的实例来实现的。
*
* @create @author Henry @date 2016-12-13
*/
class IOBlocked implements Runnable {
private InputStream in;
public IOBlocked(InputStream is) {
in = is;
}
@Override
public void run() {
try {
System.out.println("Waiting for read():");
in.read();
} catch (IOException e) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("Interrupted from blocked I/O");
} else {
throw new RuntimeException(e);
}
}
System.out.println("Exiting IOBlocked.run()");
}
}
/**
* SynchronizedBlocked 是不可中断的阻塞示例。
* 在synchronized块上的等待是不可中断的。
* 这个匿名Thread类的对象通过调用f()获取了对象锁
* (这个线程必须有别于为SynchronizedBlocked驱动run()的线程,因为一个线程可以多次获得某个对象锁)
* 由于f()永远都不返回,因此这个锁永远不会释放。而SynchronizedBlock.run()在试图调用f(),
* 并阻塞以等待这个锁被释放。
*
* @create @author Henry @date 2016-12-13
*
*/
class SynchronizedBlocked implements Runnable {
public synchronized void f() {
System.out.println("start f()");
while (true)
// Never releases lock
Thread.yield();
}
public SynchronizedBlocked() {
new Thread() {
@Override
public void run() {
f();// Lock acquired by this thread
}
}.start();
}
@Override
public void run() {
System.out.println("Trying to call f()");
f();
System.out.println("Exiting SynchronizedBlocked.run()");
}
}
/**
* 从输出中可以看到,你能够中断对sleep()的调用(或者任何要求抛出InterruptedException的调用)。
* 但是,你不能呢中断正在试图获取synchronized锁或者执行I/O具有锁住你的多线程程序的潜在可能。
* 特别是对于基于Web的程序,这更是关乎利害。
*
* 输出结果如下:
* Interrupting com.think.no21.no4.SleepBlocked
* Interrupt set to com.think.no21.no4.SleepBlocked
* InterruptedException
* Exiting SleepBlocked.run()
* Waiting for read():
* Interrupting com.think.no21.no4.IOBlocked
* Interrupt set to com.think.no21.no4.IOBlocked
* start f()
* Trying to call f()
* Interrupting com.think.no21.no4.SynchronizedBlocked
* Interrupt set to com.think.no21.no4.SynchronizedBlocked
* Aborting with System.exit(0)
*
* @create @author Henry @date 2016-12-13
*
*/
public class Interrupting {
private static ExecutorService exec = Executors.newCachedThreadPool();
static void test(Runnable r) throws InterruptedException {
Future<?> f = exec.submit(r);
TimeUnit.MILLISECONDS.sleep(100);
System.out.println("Interrupting " + r.getClass().getName());
f.cancel(true);
System.out.println("Interrupt set to " + r.getClass().getName());
}
public static void main(String[] args) throws Exception {
test(new SleepBlocked());
test(new IOBlocked(System.in));
test(new SynchronizedBlocked());
TimeUnit.SECONDS.sleep(3);
System.out.println("Aborting with System.exit(0)");
System.exit(0);// ....since last 2 interrupts failed
}
}
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* 对于这类问题,有一个略显笨拙但是有时确实行之有效的解决方案,
* 即关闭任务在其上发生阻塞的底层资源。
* 在shutdownNow()被调用之后以及在两个输入流上调用close()之前的延迟强调的是
* 一旦底层资源被关闭,任务将被解除阻塞。请注意,有一点很有趣,interrupt()看
* 起来发生在关闭Socket而不是关闭System.in的时刻。
* 在本人用Eclipse执行时发现System.in 是停不掉的。只有将整个项目停了再能将其停掉。
* 但是在Doc下运行时,是可以直接停掉。
*
* 运行结果如下:
* Waiting for read():
* Waiting for read():
* Shutting down all threads
* Closing java.net.SocketInputStream
* Interrupted from blocked I/O
* Exiting IOBlocked.run()
* Closing java.io.BufferedInputStream
* Exiting IOBlocked.run()
*
* @create @author Henry @date 2016-12-14
*
*/
public class CloseResource {
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newCachedThreadPool();
ServerSocket server = new ServerSocket(8088);
InputStream socketInput = new Socket("localhost", 8088).getInputStream();
exec.execute(new IOBlocked(socketInput));
exec.execute(new IOBlocked(System.in));
TimeUnit.MILLISECONDS.sleep(100);
System.out.println("Shutting down all threads");
exec.shutdownNow();
TimeUnit.SECONDS.sleep(1);
System.out.println("Closing " + socketInput.getClass().getName());
socketInput.close();// Releases blocked thread
TimeUnit.SECONDS.sleep(1);
System.out.println("Closing " + System.in.getClass().getName());
System.in.close();// Releases blocked thread
}
}
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.SocketChannel;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* 幸运的是,在第18章中介绍的各种nio类提供了更人性化的I/O中断。被阻塞的nio通道会自动响应中断。
*
* @create @author Henry @date 2016-12-14
*
*/
class NIOBlocked implements Runnable {
private final SocketChannel sc;
public NIOBlocked(SocketChannel sc) {
this.sc = sc;
}
@Override
public void run() {
try {
System.out.println("Waiting for read() in " + this);
sc.read(ByteBuffer.allocate(1));
} catch (ClosedByInterruptException e) {
System.out.println("ClosedByInterruptException");
} catch (AsynchronousCloseException e) {
System.out.println("AsynchronousCloseException");
} catch (IOException e) {
throw new RuntimeException(e);
}
System.out.println("Exiting NIOBlocked.run() " + this);
}
}
/**
* 如你所见,你还可以关闭底层资源以释放锁,尽管这种做法一般不是必需的。注意,使用execute()来启动两个任务,
* 并调用e.shutdownNow()将可以很容易地终止所有事物,而对于捕获下面示例中的Future,只有在将终端发送给一个
* 线程,同时不发送给另一个线程时才是必需的。
*
* 运行结果如下:
* Waiting for read() in com.think.no21.no4.NIOBlocked@5740bb
* Waiting for read() in com.think.no21.no4.NIOBlocked@5ac072
* ClosedByInterruptException
* Exiting NIOBlocked.run() com.think.no21.no4.NIOBlocked@5740bb
* AsynchronousCloseException
* Exiting NIOBlocked.run() com.think.no21.no4.NIOBlocked@5ac072
*
* @create @author Henry @date 2016-12-14
*
*/
public class NIOInterruption {
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newCachedThreadPool();
ServerSocket server = new ServerSocket(8088);
InetSocketAddress isa = new InetSocketAddress("localhost", 8088);
SocketChannel sc1 = SocketChannel.open(isa);
SocketChannel sc2 = SocketChannel.open(isa);
Future<?> f = exec.submit(new NIOBlocked(sc1));
exec.execute(new NIOBlocked(sc2));
exec.shutdown();
TimeUnit.SECONDS.sleep(1);
// Produce and interrupt via cancel;
f.cancel(true);
TimeUnit.SECONDS.sleep(1);
sc2.close();
}
}
/**
* 被互斥所阻塞
* 就像在Interrupting.java 中看到的,如果你尝试着在一个对象上调用其synchronized方法。
* 而这个对象的锁已经被其他任务获得,那么调用任务将被挂起(阻塞),直至这个锁获得。下面的
* 示例说明了同一个互斥可以如何能被同一个任务多次获得。
*
* @create @author Henry @date 2016-12-14
*
*/
public class MultiLock {
public synchronized void f1(int count) {
if (count-- > 0) {
System.out.println(" f1() calling f2() with count " + count);
f2(count);
}
}
public synchronized void f2(int count) {
if (count-- > 0) {
System.out.println(" f2() calling f1() with count " + count);
f1(count);
}
}
/**
* 在main()中创建了一个调用f1()的Thread,然后f1()和f2()互相调用直至count变为0.
* 由于这个任务已近在第一个对f1()的调用种获得了multiLock对象锁,因此同一个任务将在
* f2()的调用种再次获得这个锁,依次类推。这么做事有意义的,因为一个任务应该能够调用在
* 同一个对象中的其他的synchronized方法,而这个任务已经持有锁了。
*
* 运行结果如下:
* f1() calling f2() with count 9
* f2() calling f1() with count 8
* f1() calling f2() with count 7
* f2() calling f1() with count 6
* f1() calling f2() with count 5
* f2() calling f1() with count 4
* f1() calling f2() with count 3
* f2() calling f1() with count 2
* f1() calling f2() with count 1
* f2() calling f1() with count 0
*
* @param args
*/
public static void main(String[] args) {
final MultiLock multiLock = new MultiLock();
new Thread() {
public void run() {
multiLock.f1(10);
};
}.start();
}
}
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 就像前面在不可中断的I/O中所观察到的那样,无论在任何时刻,只要任务以不可中断的方式呗阻塞,
* 那么都有潜在的会锁住程序的可能。Java SE5并发类库中添加了一个特性,即在ReentrantLock上
* 阻塞的任务具备可以被中断的能力,这与在Synchronized 方法或临界区上阻塞的任务完全不同。
*/
/**
* BlockedMutex类有一个构造器,它要获取所创建对象上自身的Lock,并且从不释放这个锁。
* 出于这个原因,如果你试图从第二个任务中调用f()(不同于创建这个BlockedMutex的任务),
* 那么将会总是因Mutex不可获得而被阻塞。
*
* @create @author Henry @date 2016-12-14
*
*/
class BlockedMutex{
private Lock lock=new ReentrantLock();
public BlockedMutex(){
//Acquire it right away. to demonstrate interruption
//of a task blocked on a ReentrantLock;
lock.lock();
}
public void f(){
try{
//This will never be avaiable to a second task
lock.lockInterruptibly();//special call;
System.out.println("Lock acquired in f()");
}catch (InterruptedException e) {
System.out.println("Interrupted from lock acquisition in f()");
}
}
}
/**
* 在Blocked2中,run()方法总是在调用blocked.f()的地方停止。
*
*
* @create @author Henry @date 2016-12-14
*
*/
class Blocked2 implements Runnable{
BlockedMutex blocked=new BlockedMutex();
@Override
public void run() {
System.out.println("Waiting for f() in BlockedMutex");
blocked.f();
System.out.println("Broken out of blocked call");
}
}
/**
* 当运行这个程序时,你将会看到,与I/O调用不同,interrupt()可以打断被互斥所阻塞的调用。
*
* @create @author Henry @date 2016-12-14
*
*/
public class Interrupting2 {
public static void main(String[] args) throws Exception {
Thread t=new Thread(new Blocked2());
t.start();
TimeUnit.SECONDS.sleep(1);
System.out.println("Issuing t.interrupt();");
t.interrupt();
}
}
import java.util.concurrent.TimeUnit;
/**
* 检查中断
*
* 注意,当你在线程上调用interrupt()时,中断发生的唯一时刻是在任务要进入到阻塞操作中,
* 或者已经在阻塞操作内部时(如你所见,除了不可中断的I/O或被阻塞的synchronized方法之外,
* 在其余的例外情况下,你无可事事)。但是如果根据程序运行的环境,你已经编写了可能会产生
* 这种阻塞调用的代码,那又该怎么办呢?如果你只能通过在阻塞调用上抛出异常来退出,那么你
* 就无法总是可以离开run()循环。因此,如果你调用interrupt()以停止某个任务,那么在run()
* 循环碰巧没有产生任何阻塞调用的情况下,你的任务将需要第二种方式来退出。
* 这种机会是由中断状态来表示的,其状态可以通过调用interrupt()来设置。你可以通过调用
* interrupted()来检查中单状态,这不仅可以告诉你interrupt()是否被调用过,而且还可以清除
* 中断状态。清除中断状态可以确保并发结构不会就某个任务被中断这个问题通知你两次,你可以经由
* 单一的InterruptedException或单一的成功的Thread.interrupted()测试来得到这种通知。
* 如果想要再次检查以了解是否被中断,则可以在调用Thread.interrupted()时将结果存储起来。
* 下面的示例展示了典型的惯用法,你应该在run()方法中使用它来处理中断线程状态被设置时,
* 被阻塞和不被阻塞的各种可能。
*/
/**
* NeedsCleanup类强调在你经由异常离开循环时,正确清理资源的必要性。
*
* @create @author Henry @date 2016-12-15
*
*/
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);
}
}
/**
* 注意,所有在Blocked3.run()中创建的NeedsCleanup资源都必须在其后面紧跟try-finally子句,
* 以确保cleanup()方法总是会被调用。
*
* @create @author Henry @date 2016-12-15
*
*/
class Blocked3 implements Runnable {
private volatile double d = 0.0;
@Override
public void run() {
try {
while (!Thread.interrupted()) {
// point1
NeedsCleanup n1 = new NeedsCleanup(1);
// Start try-finally immediately after definition
// of n1. to guarantee proper cleanup of n1;
try {
System.out.println("Sleeping");
TimeUnit.SECONDS.sleep(1);
// point2
NeedsCleanup n2 = new NeedsCleanup(2);
try {
// Guarantee proper cleanup of n2;
System.out.println("Calculating");
// A time-consuming. non-blocking operation:
for (int i = 0; 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 InpterruptedException");
}
}
}
/**
* 通过使用不同的延迟,你可以在不同地点退出Blocked3.run():在阻塞的sleep()调用中,或者在非阻塞的
* 数学计算中。你将看到,如果interrupt()在注释point2之后(即在非阻塞的操作过程中)被调用,
* 那么首先循环将结束,然后所有本地对象将被销毁,最后循环会经由while语句的顶部退出。但是,
* 如果interrupt()在point1和point2之间(在while语句之后,但是在阻塞操作sleep()之前或其过程中)
* 被调用,那么这个任务就会在第一次试图调用阻塞操作之前,经由InterruptedException退出。
* 在这种情况下,在异常被抛出之时唯一被创建出来的NeedsCleanup对象将被清除,而你也就有了在catch
* 子句中执行其他任何清除工作的机会。
* 被设计用来响应interrupt()的类必须建立一种策略,来确保它保持一致的状态。这通常意味着所有需要
* 清理的对象创建操作的后面,都必须紧跟try-finally子句,从而使得无论run()循环如何退出,清理都会
* 发生。像这样的代码会工作得很好,但是,唉,由于在Java中缺乏自动的构造器调用,因此这将依赖于客户端
* 程序员去编写正确的try-finally子句。
*
*
* @create @author Henry @date 2016-12-15
*
*/
public class InterruptingIdiom {
public static void main(String[] args) throws Exception {
Thread t = new Thread(new Blocked3());
t.start();
TimeUnit.MILLISECONDS.sleep(1900);
t.interrupt();
}
}