我是java新手,我正在尝试理解java的concurency机制。
假设我需要使用以下指令实现任何concurency逻辑:wait(),notify(),synchronized(obj){...},synchronized方法。
是否可以在不使用wait(),notify()和synchronized(obj){...}的情况下重构代码,但只使用synchronized方法?
例如:
public class AppMe extends Thread
{
public Counter counter;
static class Counter
{
protected int num = 0;
public void increment ()
{
num++;
}
}
@Override
public void run ()
{
synchronized (counter) {
counter.increment();
}
}
public static void main (String[] args)
{
AppMe thread = new AppMe();
thread.counter = new AppMe.Counter();
thread.start();
}
}
可以重构为:
public class AppMe extends Thread
{
public Counter counter;
static class Counter
{
protected int num = 0;
synchronized public void increment ()
{
num++;
}
}
@Override
public void run ()
{
counter.increment();
}
public static void main (String[] args)
{
AppMe thread = new AppMe();
thread.counter = new AppMe.Counter();
thread.start();
}
}
这个例子非常简单,但它显示了在线程运行上下文中不使用wait(),notify(),synchronized(obj){...}的想法。
答案 0 :(得分:2)
没有。 synchronized(obj)
可以映射到synchronized
方法,但没有任何内容可以向您wait()
或notify()
购买。
答案 1 :(得分:0)
这取决于。同步是一种锁定机制,因此您可以使用同步方法获得相当远的程度(只要您了解它们的真实含义和行为)。如果你需要确保多个线程可以在一个公共对象上运行,那么普通同步就足够了。
另一方面, wait()
和notify()
是信号机制。如果您需要在线程和锁之间进行某种合作,则必须使用它们(实际上您应该使用更高级别的构造,但仍然如此)。这意味着代替共享一个公共对象的线程,它们实际上会根据其他线程的行为而有所不同。
答案 2 :(得分:0)
如果您使用自己的类,那么原则上您可以重构所有synchronized { }
块,而不是在同步对象上调用(可能是新写入的)同步方法。但是,这是一个坏主意,因为它可能会迫使您编写几十个非常具体的方法而且可重用性很低。
按顺序调用两个同步方法也与从一个syncronized { }
块一起调用它们不同。例如,给定并发map
以下未正确同步,如果多个线程执行此操作,则可能抛出NPE:
if (map.containKey (x))
map.remove (x).foo ();
通常最好使用非线程安全对象并根据需要手动同步,例如:
synchronized (map) {
if (map.containKey (x))
map.remove (x).foo ();
}
其中map
本身并不需要线程安全。
但是,线程安全性是一个重要且有用的属性的好例子是记录器对象。
答案 3 :(得分:0)
我总是更喜欢来编写synchronized(o) { ... }
而不是编写同步方法。
同步方法IMO会让您以错误的方式考虑同步,因为方法不需要同步。无论在Java程序中发生什么坏事,它的方法都不会发生任何不好的事情:只有数据才会发生坏事。
如果您想避免麻烦,请在最终私有变量引用的对象上进行同步,并使用一个名称来提醒您正在保护哪些数据。如,
private final Object payrollLock = new Object();
synchronized(payrollLock) {
doSomethingTo(payrollThis, payrollThat, payrollTheOther);
}
Java甚至允许你将synchronized
放在方法上的唯一原因是,当Java被发明时,作者认为监视器将成为银弹。解决所有并发问题。粗略地说,监视器是一个方法都是synchronized
的类。 https://en.wikipedia.org/wiki/Monitor_%28synchronization%29不幸的是,虽然这种编程风格使得编写安全程序变得容易,但是编写高效的程序却很难。
我不喜欢在方法上看到synchronized
的另一个原因是,因为它会诱使你在不需要时将代码行同步。
同步块总是应该尽可能小。方法应该尽可能小,但随着开发人员在这里添加一行,并在那里添加一行,它们有一种随着时间的推移而增长的方式。
在此示例中,程序员在方法中添加了一行以修复错误。新行不需要在任何锁上同步,因此程序员将其置于预先存在的synchronized
语句之外。
public void frobbniz(...) {
...
borkWidget.twiddleWith(numBinger, flee); // Fixes bug 28917
synchronized(frob_lock) {
...
}
}
如果整个frobbniz()方法都是synchronized
,那么即使它不需要同步,懒惰的程序员也可能只是将新行放在那里。根据twiddleWith(...)调用执行的时间长短,以及取决于frob_lock争用的其他线程,这可能会对程序产生重大的性能影响。
我不希望在方法上看到synchronized
的另一个原因是因为它使锁定对象对使用您的类的人可见。只需在StackOverflow上搜索那些对在wait()/notify()
对象上尝试使用Thread
时发生的事情感到惊讶的人提出的所有问题,您就会明白为什么将锁定对象保密为一个好主意。