# HG changeset patch
# User chegar
# Date 1491465647 -3600
# Node ID afbe031283f7f1818ef03b66546f45ad981cadb7
# Parent 1b6b813e4831e9629eb6a9f11a7b1a9dbc35b895
8178101: Migrate the thread deprecation technote to javadoc doc-files
Reviewed-by: dholmes
diff -r 1b6b813e4831 -r afbe031283f7 jdk/src/java.base/share/classes/java/lang/Thread.java
--- a/jdk/src/java.base/share/classes/java/lang/Thread.java Thu Apr 06 09:33:47 2017 +0800
+++ b/jdk/src/java.base/share/classes/java/lang/Thread.java Thu Apr 06 09:00:47 2017 +0100
@@ -927,7 +927,7 @@
* for example), the interrupt
method should be used to
* interrupt the wait.
* For more information, see
- * Why
+ * Why
* are Thread.stop, Thread.suspend and Thread.resume Deprecated?.
*/
@Deprecated(since="1.2")
@@ -960,7 +960,7 @@
* could be used to generate exceptions that the target thread was
* not prepared to handle.
* For more information, see
- * Why
+ * Why
* are Thread.stop, Thread.suspend and Thread.resume Deprecated?.
* This method is subject to removal in a future version of Java SE.
*/
@@ -1082,7 +1082,7 @@
* If another thread ever attempted to lock this resource, deadlock
* would result. Such deadlocks typically manifest themselves as
* "frozen" processes. For more information, see
- *
+ *
* Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?.
* This method is subject to removal in a future version of Java SE.
* @throws NoSuchMethodError always
@@ -1122,7 +1122,7 @@
* monitor prior to calling resume
, deadlock results. Such
* deadlocks typically manifest themselves as "frozen" processes.
* For more information, see
- * Why
+ * Why
* are Thread.stop, Thread.suspend and Thread.resume Deprecated?.
*/
@Deprecated(since="1.2")
@@ -1148,7 +1148,7 @@
* @deprecated This method exists solely for use with {@link #suspend},
* which has been deprecated because it is deadlock-prone.
* For more information, see
- * Why
+ * Why
* are Thread.stop, Thread.suspend and Thread.resume Deprecated?.
*/
@Deprecated(since="1.2")
diff -r 1b6b813e4831 -r afbe031283f7 jdk/src/java.base/share/classes/java/lang/doc-files/threadPrimitiveDeprecation.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/doc-files/threadPrimitiveDeprecation.html Thu Apr 06 09:00:47 2017 +0100
@@ -0,0 +1,364 @@
+
+
+
+
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.
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:
+ThreadDeath
exception
+almost anywhere. All synchronized methods and blocks would
+have to be studied in great detail, with this in mind.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.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
?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:
+ 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(); + } + } ++
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
?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.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
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
.
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 resultingrun
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(); + } + } ++
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. +
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
.)
+Runtime.runFinalizersOnExit
+deprecated?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!
+ + +