【Java】【多線程】終止線程

Java Thread Primitive Deprecation
________________________________________
Why is Thread.stop deprecated?
因爲它本質上是不安全。停止線程會使其釋放所有已鎖定的監視器。(當ThreadDeath異常傳播到堆棧時,監視器將被解鎖。)如果先前受這些監視器保護的任何對象處於不一致狀態,則其他線程現在可能會以不一致的狀態查看這些對象。這個時候對象就已經被破壞了。當線程對損壞的對象進行操作時,會產生任意行爲。這種行爲可能很微妙,難以檢測,或者很明顯。不同於其他未經檢查的異常,ThreadDeath默認殺死線程; 因此,用戶沒有被警告自己的程序可能被破壞。 程序被破壞的現象可能在實際損害發生後的任何時候,甚至在未來的幾個小時或幾天內能夠體現。
補充:
調用stop()方法時會拋出java.lang.ThreadDeath異常,但在通常的情況下,此異常不需要顯式地捕捉。
   
 
方法stop()已經被作廢,因爲如果強制讓線程停止則有可能使一些清理性的工作得不到完成。另外一個情況就是對鎖定的對象進行了“解鎖”,導致數據得不到同步的處理,出現數據不一致的問題
________________________________________
Couldn't I just catch the ThreadDeath exception and fix the damaged object?
在理論上是可以的,但它會使多線程代碼異常複雜化。但是在實際操作過程中, 這個任務幾乎是無法完成的,原因有二:
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);
    }
________________________________________
What should I use instead of Thread.stop?
大多數停止的使用應該被替換爲只是修改一些變量以指示目標線程應該停止運行的代碼。 目標線程應該定期檢查此變量,如果變量表示要停止運行,則以有序方式從其運行方法返回。 爲了確保stop-request的及時通信,變量必須是volatile(或者變量的訪問必須同步)
For example, suppose your applet contains the following start, stop and run methods:
    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:
    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();
        }
    }
________________________________________
How do I stop a thread that waits for long periods (e.g., for input)?
上面的處理方式是存在問題的,如果當線程處於非運行狀態的時候(當sleep方法被調用或者當wait方法被調用或者被IO阻塞的),上面的方法是不可以使用。此時可以使用interrupt方法來打破阻塞的情況,當interrupt方法來打破阻塞的情況,當interrupt被調用的時候,會拋出InetrruptedException異常,可以通過在run方法中捕獲這個異常來讓線程安全退出
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.
________________________________________
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.
替換方法:也是去觸發一個異常,而這個異常與所使用的IO是相關的,例如等待網絡信息進入阻塞狀態,這個時候可以調用close關閉流,會引發IOException異常,run方法可以通過捕獲這個異常來安全結束線程。
________________________________________
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.
________________________________________
What should I use instead of Thread.suspend and Thread.resume?
與Thread.stop一樣,謹慎的做法是讓“目標線程”輪詢一個指示線程所需狀態(活動或掛起)的變量。 當所需的狀態被暫停時,線程將使用Object.wait等待。 當線程恢復時,使用Object.notify通知目標線程。
For example, suppose your applet contains the following mousePressed event handler, which toggles the state of a thread called blinker:
    private boolean threadSuspended;


    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:
    public synchronized void mousePressed(MouseEvent e) {
        e.consume();


        threadSuspended = !threadSuspended;


        if (!threadSuspended)
            notify();
    }
and adding the following code to the "run loop":
                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:
    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:
                if (threadSuspended) {
                    synchronized(this) {
                        while (threadSuspended)
                            wait();
                    }
                }
In the absence of explicit synchronization, threadSuspended must be made volatile to ensure prompt communication of the suspend-request.
The resulting run method is:
    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:
    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.
________________________________________
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.)
________________________________________
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!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章