同步发生在成员类还是包装类中?
首先,我读过通过:同步发生在成员类还是包装类中?
- In Java critical sections, what should I synchronize on?
- synchronized(this) blocks whole object? [duplicate]
- Does thread.yield() lose the lock on object if called inside a synchronized method?
- What is the difference between a synchronized method and synchronized block in Java?
- Java synchronized difference threading [duplicate]
- What is the difference between a synchronized function and synchronized block?
我跟随无数的链接重复列出大多数这些职位。所以我提前道歉,如果这是重复的。我不觉得我的问题被任何这些或其后的链接回答。但是现在我又问了,因为我不知道这里发生了什么。现在的主要事件...
我有一对类,A
和B
。 B
类具有A
实例作为成员:
A类:
public class A {
private final int count;
public A(int val) {
count = val;
}
public int count() { return count; }
public A incrementCount() {
return new A(count + 1);
}
public void doStuff(long i) {
try {
Thread.sleep(i * 100);
} catch (Exception e) {
e.printStackTrace();
}
}
}
类B:
public class B implements Runnable{
private A a;
public B() { a = new A(0); }
@Override
public void run() {
for (int i = 1; i < 5; i++) {
a.doStuff(i);
a = a.incrementCount();
}
}
}
我有一个类,需要B的一个实例,并传递它到两个线程,启动两个线程,然后让他们做他们的事情:
public class App {
public static void main(String[] args) {
B b = new B();
Thread b1 = new Thread(b, "b1");
b1.start();
Thread b2 = new Thread(b, "b2");
b2.start();
try {
b1.join();
b2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
我期望的是,对于由b
,count
包含的A的实例,其顺序从0递增到8。
但此代码:
synchronized public A incrementCount() {
return new A(count + 1);
}
synchronized public void doStuff(long i) {
try {
Thread.sleep(i * 100);
} catch (Exception e) {
e.printStackTrace();
}
}
或本规范(等效于上面,我认为):
public A incrementCount() {
synchronized (this) {
return new A(count + 1);
}
}
public void doStuff(long i) {
synchronized (this){
try {
Thread.sleep(i * 100);
} catch (Exception e) {
e.printStackTrace();
}
}
}
我得到的结果是这样的:
THREAD| OBJECT |COUNT
----------------------------
main |[email protected]| 0
b1 |[email protected]| 1
b1 |[email protected]| 2
b2 |[email protected]| 2
b2 |[email protected]| 3
b1 |[email protected]| 4
b2 |[email protected]| 4
b1 |[email protected]| 5
b2 |[email protected]| 5
显然,东西是不对的。我也认为值得注意的是,尽管有重复的数字,但这些对象似乎都是独特的对象。
但对于这个代码(A类):
private final Object incrementLock = new Object();
private final Object doStuffLock = new Object();
...
public A incrementCount() {
synchronized (incrementLock) {
return new A(count + 1);
}
}
public void doStuff(long i) {
synchronized (doStuffLock){
try {
Thread.sleep(i * 100);
} catch (Exception e) {
e.printStackTrace();
}
}
}
或本规范(B类):
@Override
synchronized public void run() {
for (int i = 1; i < 5; i++) {
a.doStuff(i);
a = a.incrementCount();
}
}
我得到的结果我想到:
THREAD| OBJECT |COUNT
------------------------------
main |[email protected] | 0
b1 |[email protected] | 1
b2 |[email protected] | 2
b1 |[email protected] | 3
b2 |[email protected]| 4
b1 |[email protected]| 5
b2 |[email protected] | 6
b1 |[email protected]| 7
b2 |[email protected] | 8
由于只有一个对象被两个线程访问(b1
和b2
),为什么aren 't synchronized (this)
或synchronized public...
锁定对象实例,阻止两个线程进入同步块并破坏count
,可以这么说吗?或者我错过了什么?
您应该同步B
中的代码,其中您有多个线程会突变状态(实例变量a
)。同步A
中的方法是没有意义的,因为类的实例实际上只是不可变的值对象。
当A
同步上this
方法,在代码中最棘手的部分是这样的:
a = a.incrementCount();
因为有您泄漏监视器类之外,并重新分配这是抱着它的变量。
即使使用不同的监控对象的两种方法的A
版本似乎工作,有一个竞争条件(你可以看到,如果你添加更多的线程和迭代步骤和降低/消除doStuff()
睡眠时间)因为没有什么能保证在上面的代码中正确增加了a
。
使代码线程安全的唯一方法是同步run()
方法B
。
在incrementCount()
中,您每次创建一个新的A
实例时,几乎都会损害您的整体同步想法。这是你的问题。只需增加计数器,不要每次更换/重新创建A
实例。
有没有什么机会让我说服你详细说明你的意思* ......在课堂外泄漏显示器*? – nihilon 2015-03-19 14:19:06