1. 程式人生 > >Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?翻譯

Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?翻譯

前面我們的學習筆記中講解Thread類中一些廢棄的方法及原因,同時又示例程式碼。

"""本文是我學習Java多執行緒以及高併發知識的第一本書的學習筆記,
書名是<<Java多執行緒程式設計核心技術>>,作者是大佬企業高階專案經理
高洪巖前輩,在此向他致敬。我將配合開發文件以及本書和其他的部落格
奉獻著的文章來學習,同時做一些簡單的總結。有些基礎的東西我就不
細分析了,建議以前接觸過其他語言多執行緒或者沒有系統學習過多執行緒
的開發者來看。另外需要注意的是,部落格中給出的一些英文文件我就簡單
翻譯了,重要的部分會詳細翻譯,要不太浪費時間了,這個是我想提高
自己的英文閱讀水平和文件檢視能力,想要積攢內功的人可以用有谷歌
翻譯自己看文件細讀。(中文文件建議只參考,畢竟你懂得...)
詳細程式碼見:https://github.com/youaresherlock/multithreadingforjavanotes

Why are Thread.stop, Thread.suspend and Thread.resume
Deprecated?英文文件翻譯
通過閱讀一篇文件來重新體會Thread類中的stop()、suspend()、resume()方法過時的原因
我的jdk版本是9.0.4,所以翻譯的文章是9給出的,文章內容其實差別不大
java9和java11分別如下文章地址如下: 
https://docs.oracle.com/javase/9/docs/api/java/lang/doc-files/threadPrimitiveDeprecation.html
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/doc-files/threadPrimitiveDeprecation.html

英漢如下:
Java Thread Primitive Deprecation
Why is Thread.stop deprecated?
Because it is inherently unsafe. Stopping a thread causes it to unlock all the monitors 
that it has locked. (The monitors are unlocked as the ThreadDeath exception propagates
up the stack.) If any of the objects previously protected by these monitors were in an 
inconsistent state, other threads may now view these objects in an inconsistent state. 
Such objects are said to be damaged. When threads operate on damaged objects, arbitrary 
behavior can result. This behavior may be subtle and difficult to detect, or it may be 
pronounced. Unlike other unchecked exceptions, ThreadDeath kills threads silently; thus, 
the user has no warning that his program may be corrupted. The corruption can manifest 
itself at any time after the actual damage occurs, even hours or days in the future.
Java執行緒類Thread原始棄用
為什麼Thread.stop方法被棄用?
因為它本質上是不安全的。停止一個執行緒導致它會釋放所有已鎖定的監視器。(當ThreadDeath
異常在堆疊中傳播時,監視器將被解鎖。)如果先前受這些監視器保護的物件處於不一致狀態,
則其他執行緒則其他執行緒現在可能會以不一致的狀態檢視這些物件。這些物件就被破壞了。當執行緒
對被破壞的物件進行操作時,會產生任意的行為。這種行為可能很微妙並且難以檢測,或者很明
顯。不同於其他的非檢查異常,ThreadDeath異常悄無聲息地殺死執行緒;因此,使用者在自己的程
序可能被破壞情況下沒有被警告。在實際破壞發生之後,程式被破壞的現象可能發生在任何時候,
甚至是未來的幾個小時或幾天內能夠體現。



Couldn't I just catch the ThreadDeath exception and fix the damaged object?
In theory, perhaps, but it would vastly complicate the task of writing correct 
multithreaded code. The task would be nearly insurmountable for two reasons:

	1.A thread can throw a ThreadDeath exception almost anywhere. All synchronized 
methods and blocks would have to be studied in great detail, with this in mind.
	2.A thread can throw a second ThreadDeath exception while cleaning up from the 
first (in the catch or finally clause). Cleanup would have to be repeated till 
it succeeded. The code to ensure this would be quite complex.

In sum, it just isn't practical.
難道我不能捕獲ThreadDeath異常並修復損壞的物件嗎?
理論上來說,或許可以,但是完成寫出正確的多執行緒程式碼是極其複雜的。這項任務幾乎無法克服,
原因有兩點:
	1.一個執行緒幾乎可以在任何地方丟擲一個ThreadDeath異常。所有同步的方法和同步語句塊
必須非常詳細地研究,考慮到這一點。
	2.當位於catch或finally子句中的第一個清理一個執行緒可以丟擲第二個ThreadDeath異常。
清理將不得不重複,直到成功。確保這一點的程式碼是相當複雜的。
總而言之,這種做法是不切實際的。



What about Thread.stop(Throwable)?
In addition to all of the problems noted above, this method may be used to generate 
exceptions that its target thread is unprepared to handle (including checked 
exceptions that the thread could not possibly throw, were it not for this method). 
For example, the following method is behaviorally identical to Java's throw operation, 
but circumvents the compiler's attempts to guarantee that the calling method has 
declared all of the checked exceptions that it may throw:
static void sneakyThrow(Throwable t) {
    Thread.currentThread().stop(t);
}
那麼關於Thread.stop(Throwable obj)呢?
除了上面提到的所有問題,這個方法還可以用來生成其目標執行緒沒有準備處理的異常。(如果不是
這個方法的話,包括當前執行緒不可能丟擲的檢查異常)
舉個例子,下面的方法在行為上與java的trow操作相同,但是規避了編譯器嘗試來保證所呼叫的
方法聲明瞭它可能會丟擲的所有檢查異常



What should I use instead of Thread.stop?
Most uses of stop should be replaced by code that simply modifies some variable 
to indicate that the target thread should stop running. The target thread should 
check this variable regularly, and return from its run method in an orderly fashion 
if the variable indicates that it is to stop running. To ensure prompt communication 
of the stop-request, the variable must be volatile (or access to the variable 
must be synchronized).

For example, suppose your applet contains the following start, stop and run methods:
我應當用什麼來替代Thread.stop()方法呢?
大多數停止的使用應該被替換為只是簡單的修改一些變數來指示目標執行緒應該停止執行的程式碼。目
標執行緒應該定期檢查此變數,如果變量表示它將要停止執行,則要以有序方式從目標執行緒的run()
方法中返回。為了保證停止請求的及時通訊,變數必須是volatile(或者對變數的訪問必須是同步的)

例如,假設你的應用包含下面的啟動、停止和執行方法。
private Thread blinker;

public void start() {
    blinker = new Thread(this);
    blinker.start();
}

public void stop() {
    blinker.stop();  // UNSAFE! 可以看到這種方法不安全
}

public void run() {
    while (true) {
        try {
            Thread.sleep(interval);
        } catch (InterruptedException e){
        }
        repaint();
    }
}
You can avoid the use of Thread.stop by replacing the applet's stop and run methods with:
你可以通過一下方式替換應用的stop和run法來避免使用Thread.stop方法
private volatile Thread blinker;

public void stop() {
    blinker = null;
}

public void run() {
    Thread thisThread = Thread.currentThread();
    while (blinker == thisThread) {
        try {
            Thread.sleep(interval);
        } catch (InterruptedException e){
        }
        repaint();
    }
}
上面的處理方式是存在問題的,如果當執行緒處於非執行狀態的時候(當sleep方法被呼叫或者當wait
方法被呼叫或者被IO阻塞的),上面的方法是不可以使用。此時可以使用interrupt方法來打破阻塞
的情況,當interrupt方法來打破阻塞的情況,當interrupt被呼叫的時候,會丟擲Inetrrupted
Exception異常,可以通過在run方法中捕獲這個異常來讓執行緒安全退出



How do I stop a thread that waits for long periods (e.g., for input)?
That's what the Thread.interrupt method is for. The same "state based" 
signaling mechanism shown above can be used, but the state change (blinker = null
, in the previous example) can be followed by a call to Thread.interrupt, 
to interrupt the wait:

public void stop() {
    Thread moribund = waiter;
    waiter = null;
    moribund.interrupt();
}

For this technique to work, it's critical that any method that catches an interrupt 
exception and is not prepared to deal with it immediately reasserts the exception. 
We say reasserts rather than rethrows, because it is not always possible to rethrow 
the exception. If the method that catches the InterruptedException is not declared 
to throw this (checked) exception, then it should "reinterrupt itself" with the 
following incantation:
    Thread.currentThread().interrupt();
This ensures that the Thread will reraise the InterruptedException as soon as it is able.
我如何停止一個需要等待很長時間的執行緒(例如,等待輸入)?
這就是Thread.interrupt方法的用途,可以使用相同的如上所示的"基於狀態"的訊號機制,但是狀
態的改變只有可以呼叫interrupt方法來中斷等待:
要使這種技術起作用,任何捕獲InterruptedException的異常是至關重要的,並且沒有準備好立刻
處理這個異常。我們說重新主張而不是重新丟擲,因為並不總是可能重新丟擲異常。如果捕獲InterrupedException
異常的方法沒有宣告丟擲這個檢查異常,那麼它應該用下面的方法重新中斷它自己:
	Thread.currentThread().interrupt();
這個確保執行緒在允許的情況下立刻觸發InterruptedException異常。



What if a thread doesn't respond to Thread.interrupt?
In some cases, you can use application specific tricks. For example, if a thread is 
waiting on a known socket, you can close the socket to cause the thread to return 
immediately. Unfortunately, there really isn't any technique that works in general. 
It should be noted that in all situations where a waiting thread doesn't respond to 
Thread.interrupt, it wouldn't respond to Thread.stop either. Such cases include 
deliberate denial-of-service attacks, and I/O operations for which thread.stop and 
thread.interrupt do not work properly.
如果一個執行緒沒有響應Thread.interrupt方法怎麼辦?
在某些情況下,你可使用特定的技巧。例如,如果一個執行緒是在已知的套接字上等待,可以關閉套接
字來使得執行緒立刻返回。 不幸的是, 在一般情況下並沒有任何有效的技術。需要注意的是,在一個
等待執行緒對Thread.interrupt方法沒有響應的所有情況下,它也不會響應Thread.stop方法。這種
情況下,包括故意的拒絕服務攻擊,和I/O操作,thread.stop和thread.interrupt方法不能正確
地工作。



Why are Thread.suspend and Thread.resume deprecated?
Thread.suspend is inherently deadlock-prone. If the target thread holds a lock on 
the monitor protecting a critical system resource when it is suspended, no thread 
can access this resource until the target thread is resumed. If the thread that 
would resume the target thread attempts to lock this monitor prior to calling resume, 
deadlock results. Such deadlocks typically manifest themselves as "frozen" processes.
為什麼Thread.suspend和Thread.resume方法被淘汰?
Thread.suspend方法在本質上是容易死鎖的。當目標執行緒呼叫了Thread.suspend方法,如果它在監視器上
擁有一個鎖來保護重要的系統資源,那麼直到目標執行緒呼叫Thread.resume方法,沒有執行緒可以訪問這個資
源。如果將呼叫resume方法來恢復目標執行緒的這個執行緒嘗試在呼叫Thread.resume方法之前鎖定這個監視器
,會造成死鎖。這種死鎖通常表現為"凍結"程序。



What should I use instead of Thread.suspend and Thread.resume?

As with Thread.stop, the prudent approach is to have the "target thread" poll a variable 
indicating the desired state of the thread (active or suspended). When the desired state 
is suspended, the thread waits using Object.wait. When the thread is resumed, the target 
thread is notified using Object.notify.
我應該用什麼代替Thread.suspend和Thread.resume方法?

正如Thread.stop方法一樣,謹慎的方法是讓"目標執行緒"輪詢一個變數,這個變數指示期望的執行緒狀態(活動
狀態(也就是執行狀態)或掛起狀態)。當所需的狀態是掛起狀態的時候,執行緒使用Object.wait方法來等待.(
超類Object方法中定義了wait()方法和notify以及notifyAll()方法,其他的物件都繼承了這個方法, 另外
wait方法當前執行緒會釋放鎖)。當所執行緒恢復時,使用Object.notify方法來通知執行緒。

For example, suppose your applet contains the following mousePressed event handler, which 
toggles the state of a thread called blinker:
舉個例子,假設你的應用程式包含了滑鼠按下事件的處理程式,它切換名為bliker的執行緒的狀態:
private boolean threadSuspended; //這個就是執行緒掛起和恢復的flag

Public void mousePressed(MouseEvent e) {
    e.consume();

    if (threadSuspended)
        blinker.resume(); 
    else
        blinker.suspend();  // DEADLOCK-PRONE!

    threadSuspended = !threadSuspended;
}
You can avoid the use of Thread.suspend and Thread.resume by replacing the event handler above with:
你可以通過用下面的替換事件處理器來避免使用Thread.suspend和Thread.resume方法
public synchronized void mousePressed(MouseEvent e) {
    e.consume();

    threadSuspended = !threadSuspended;

    if (!threadSuspended)
        notify();
}
and adding the following code to the "run loop":
並且在run()方法中加入下面的程式碼:
synchronized(this) {
    while (threadSuspended)
        wait();
}
The wait method throws the InterruptedException, so it must be inside a try ... catch 
clause. It's fine to put it in the same clause as the sleep. The check should follow 
(rather than precede) the sleep so the window is immediately repainted when the thread 
is "resumed." The resulting run method follows:
wait()方法會丟擲InterruptedException異常,因此它必須包含在try-catch語句塊中。把它和sleep()方
法放在通過一個語句塊裡是可以的。檢查應該在sleep()方法呼叫之後(而不是之前),以便於執行緒被恢復時視窗
立即重繪。示例的run()方法程式碼如下:

public void run() {
    while (true) {
        try {
            Thread.sleep(interval);

            synchronized(this) {
                while (threadSuspended)
                    wait();
            }
        } catch (InterruptedException e){
        }
        repaint();
    }
}
Note that the notify in the mousePressed method and the wait in the run method are 
inside synchronized blocks. This is required by the language, and ensures that wait 
and notify are properly serialized. In practical terms, this eliminates race conditions 
that could cause the "suspended" thread to miss a notify and remain suspended indefinitely.
While the cost of synchronization in Java is decreasing as the platform matures, it 
will never be free. A simple trick can be used to remove the synchronization that 
we've added to each iteration of the "run loop." The synchronized block that was 
added is replaced by a slightly more complex piece of code that enters a synchronized 
block only if the thread has actually been suspended:
注意: mousePressed方法中的notify方法和run方法中的wait方法是在sychronized塊中.這是
語言所需要的,並且確保wait和notify被正確序列化。實際上,這消除了競態條件,而這種競態
條件可能導致"掛起"執行緒錯過通知並無限期地掛起。儘管隨著平臺的成熟,Java中同步成本正在
不斷下降,但永遠不會沒有開銷。可以使用一個簡單的技巧來移除同步,我們可以新增run方法的
每個迭代。被新增的同步塊被一個稍微複雜一點的的程式碼所替代,僅僅當執行緒實際上已經掛起時才
進入同步的程式碼塊。
In the absence of explicit synchronization, threadSuspended must be made volatile to 
ensure prompt communication of the suspend-request.

The resulting run method is:
在沒有顯示同步的情況下,必須用volatile關鍵字修飾threadSuspended變數來確保suspend-request的
及時溝通。
private volatile boolean threadSuspended;

public void run() {
    while (true) {
        try {
            Thread.sleep(interval);

            if (threadSuspended) {
                synchronized(this) {
                    while (threadSuspended)
                        wait();
                }
            }
        } catch (InterruptedException e){
        }
        repaint();
    }
}



Can I combine the two techniques to produce a thread that may be safely "stopped" 
or "suspended"? 
我可以將這兩種技術結合起來,產生一個安全的"停止的"或"掛起的"執行緒嗎?
Yes, it's reasonably straightforward. The one subtlety is that the target thread 
may already be suspended at the time that another thread tries to stop it. If the 
stop method merely sets the state variable (blinker) to null, the target thread 
will remain suspended (waiting on the monitor), rather than exiting gracefully 
as it should. If the applet is restarted, multiple threads could end up waiting 
on the monitor at the same time, resulting in erratic behavior.
To rectify this situation, the stop method must ensure that the target thread resumes 
immediately if it is suspended. Once the target thread resumes, it must recognize 
immediately that it has been stopped, and exit gracefully. Here's how the resulting 
run and stop methods look:
是的,它實現是很簡單的。一個微妙之處在於目標執行緒在當另一個執行緒試圖停止它時可能已經被掛起。
如果stop()方法僅僅將變數blinker的狀態設定為null,目標執行緒將保持掛起(等待監視器),而不是
當它可以的時候優雅的退出。如果應用重新啟動,多個執行緒可能同時停止等待監視器,導致不穩定的
行為。為了糾正這種情況,自定義的stop()方法應該確保當目標執行緒被掛起,它能立刻恢復。一旦目標
執行緒恢復,它必須立刻識別到並停止優雅地退出。下面是定義的run和stop方法:
public void run() {
    Thread thisThread = Thread.currentThread();
    while (blinker == thisThread) {
        try {
            Thread.sleep(interval);

            synchronized(this) {
                while (threadSuspended && blinker==thisThread)
                    wait();
            }
        } catch (InterruptedException e){
        }
        repaint();
    }
}

public synchronized void stop() {
    blinker = null;
    notify();
}
If the stop method calls Thread.interrupt, as described above, it needn't call 
notify as well, but it still must be synchronized. This ensures that the target 
thread won't miss an interrupt due to a race condition.
正如前面描述的那樣,如果自定義stop()方法呼叫Thread.interrupt()方法,則不需要呼叫notify
()方法,但它仍然必須同步。這就保證了目標執行緒不會因為競爭條件而錯過中斷。



What about Thread.destroy?

Thread.destroy was never implemented and has been deprecated. If it were implemented,
it would be deadlock-prone in the manner of Thread.suspend. (In fact, it is roughly 
equivalent to Thread.suspend without the possibility of a subsequent Thread.resume.)
對於Thread.destroy方法呢?
Thread.destroy從未被實現,現在已經被廢棄了。如果它被實現,它很容易造成死鎖。(事實上,在沒有後續
執行緒掛起的情況下,粗略與Thread.suspend方法相同)



Why is Runtime.runFinalizersOnExit deprecated?

Because it is inherently unsafe. It may result in finalizers being called on live objects 
while other threads are concurrently manipulating those objects, resulting in erratic 
behavior or deadlock. While this problem could be prevented if the class whose objects 
are being finalized were coded to "defend against" this call, most programmers do not 
defend against it. They assume that an object is dead at the time that its finalizer is called.
Further, the call is not "thread-safe" in the sense that it sets a VM-global flag. This 
forces every class with a finalizer to defend against the finalization of live objects!
為什麼Runtime.runFinalizersOnExit被廢棄?
因為它本質上是不安全的。它會導致對活動物件呼叫終結器。當其他執行緒併發操作這些物件的時候,會導致不
穩定或者死鎖。然而,如果實力物件被終結的類被編碼為"防禦"這個呼叫,這個問題可以被避免。大多數程式
員沒有抵禦它。他們假設在呼叫終止程式時物件已經死亡。此外,從設定VM-全域性標誌的意義上說,呼叫不是
"執行緒安全的"。這強制每個類使用終結器來防禦活動物件的終結!"""