同步线程对象的运行()方法
Sychronizing run() method of thread object
Caution: Don't synchronize a thread object's run() method because
situations arise where multiple threads need to execute run(). Because
those threads attempt to synchronize on the same object, only one
thread at a time can execute run(). As a result, each thread must wait
for the previous thread to terminate before it can access run().
不同的线程如何执行相同的 Thread
对象的 run()
?
关于同步的一些一般性建议似乎与此相关:不要将同步放在您的线程或 运行nables 中,将其放在线程正在访问的数据结构中。如果你用锁来保护数据结构,那么你就可以确保没有线程可以以不安全的方式访问它,因为数据结构正在强制安全访问。如果您将同步留给线程,那么有人可以编写一个新线程,该线程不进行适当的锁定,并可能破坏正在访问的数据结构。请记住,同步的目的是保护数据免受不安全的并发修改。
(如果您查看 JavaWorld 文章,清单 2 和清单 3 说明了这一点;清单 3 明显比清单 2 更明智,因为 FinTrans 数据保护其自身的完整性,而清单 2 中的线程是进行同步。作者主张清单 3 具有更好的锁定粒度,并且没有解决让数据结构保护自身完整性的问题。也许那是因为他正在制作玩具示例并且没有采用任何示例认真的;毕竟,他在页面顶部展示了使用字符串作为锁,这是一个非常糟糕的主意。)
此外,Java API 文档不鼓励您
锁定线程对象。 Java 线程实现锁定线程,例如在加入线程时,因此您所做的任何操作都可能与 Java 线程 API 所做的事情纠缠在一起;例如,如果您尝试锁定线程,则您发出的任何通知调用都可能被其他试图加入的线程消耗掉。您还可能会看到一些奇怪的事情,例如,当线程终止时,它会向在其监视器上等待的任何对象发送通知。如果使 Thread 子类的 运行 方法同步,则 运行ning 线程必须获取自己的锁。如果另一个线程想要加入它(除非子类 Thread 通过等待放弃锁)这使得任何线程都不可能加入(因为这涉及等待,这需要获取 Thread 对象上的锁),所以而不是加入线程短暂获取锁并安顿下来等待,加入线程可能会在等待集中等待锁定,直到要加入的线程终止。
还有一点是,最好将任务实现为 Runnable 而不是 Thread 对象。我想不出实现 Thread 对象的 运行 方法比实现 Runnable 更可取的情况,除非我试图故意制造混乱的情况,或者正在键入快速而肮脏的演示。 (我真的很想知道 Thread 实现 Runnable 的原因是否是为了让人们更方便地编写 fast-n-dirty 演示代码。)让你的任务成为 Runnable 清楚地表明你有一些不受限制的逻辑成为 运行 作为一个新线程,但也可以移交给一个执行者,该执行者可以负责该任务的执行方式。 (您可以使用 Thread 对象执行此操作,但它会造成混淆。)因此,不在 Thread 对象上创建同步 运行 方法的另一个原因是,您不应该子类化 Thread 以覆盖 运行 方法(通常最好使用带有 Runnables 的执行器而不是旋转你自己的线程)。
Caution: Don't synchronize a thread object's run() method because situations arise where multiple threads need to execute run(). Because those threads attempt to synchronize on the same object, only one thread at a time can execute run(). As a result, each thread must wait for the previous thread to terminate before it can access run().
不同的线程如何执行相同的 Thread
对象的 run()
?
关于同步的一些一般性建议似乎与此相关:不要将同步放在您的线程或 运行nables 中,将其放在线程正在访问的数据结构中。如果你用锁来保护数据结构,那么你就可以确保没有线程可以以不安全的方式访问它,因为数据结构正在强制安全访问。如果您将同步留给线程,那么有人可以编写一个新线程,该线程不进行适当的锁定,并可能破坏正在访问的数据结构。请记住,同步的目的是保护数据免受不安全的并发修改。
(如果您查看 JavaWorld 文章,清单 2 和清单 3 说明了这一点;清单 3 明显比清单 2 更明智,因为 FinTrans 数据保护其自身的完整性,而清单 2 中的线程是进行同步。作者主张清单 3 具有更好的锁定粒度,并且没有解决让数据结构保护自身完整性的问题。也许那是因为他正在制作玩具示例并且没有采用任何示例认真的;毕竟,他在页面顶部展示了使用字符串作为锁,这是一个非常糟糕的主意。)
此外,Java API 文档不鼓励您 锁定线程对象。 Java 线程实现锁定线程,例如在加入线程时,因此您所做的任何操作都可能与 Java 线程 API 所做的事情纠缠在一起;例如,如果您尝试锁定线程,则您发出的任何通知调用都可能被其他试图加入的线程消耗掉。您还可能会看到一些奇怪的事情,例如,当线程终止时,它会向在其监视器上等待的任何对象发送通知。如果使 Thread 子类的 运行 方法同步,则 运行ning 线程必须获取自己的锁。如果另一个线程想要加入它(除非子类 Thread 通过等待放弃锁)这使得任何线程都不可能加入(因为这涉及等待,这需要获取 Thread 对象上的锁),所以而不是加入线程短暂获取锁并安顿下来等待,加入线程可能会在等待集中等待锁定,直到要加入的线程终止。
还有一点是,最好将任务实现为 Runnable 而不是 Thread 对象。我想不出实现 Thread 对象的 运行 方法比实现 Runnable 更可取的情况,除非我试图故意制造混乱的情况,或者正在键入快速而肮脏的演示。 (我真的很想知道 Thread 实现 Runnable 的原因是否是为了让人们更方便地编写 fast-n-dirty 演示代码。)让你的任务成为 Runnable 清楚地表明你有一些不受限制的逻辑成为 运行 作为一个新线程,但也可以移交给一个执行者,该执行者可以负责该任务的执行方式。 (您可以使用 Thread 对象执行此操作,但它会造成混淆。)因此,不在 Thread 对象上创建同步 运行 方法的另一个原因是,您不应该子类化 Thread 以覆盖 运行 方法(通常最好使用带有 Runnables 的执行器而不是旋转你自己的线程)。