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();
}
}