目的:创建4个线程,在每个线程中,for循环将1加到共享的int变量10000次。最后共享变量应该是40000.I使用两种方法来做到这一点,结果每次运行代码时都不总是40000.我想知道什么是错的。
方法1 with synchronized:
public class bbb {
int i=0;
public void add() {
synchronized(bbb.class){
i++;
bbb.class.notifyAll();
System.out.println(i);
try {
bbb.class.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread t1=new Thread(new Runnable() {
bbb b=new bbb();
@Override
public void run() {
// TODO Auto-generated method stub
int i=0;
for (; i < 10000; i++) {
b.add();
}
}
},"A");
Thread t2=new Thread(t1,"B");
Thread t3=new Thread(t1,"C");
Thread t4=new Thread(t1,"D");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
带乐观锁的方法2:
static volatile int value=0;
static void optimisticLock(int expected,int update){
if(expected==value){
value=update;
System.out.println(Thread.currentThread().getName()+" "+update);
}else{
optimisticLock(value, value+1);
}
}
public static void main(String[] args) {
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<10000;i++){
optimisticLock(value, value+1);
}
}
},"A");
Thread t2=new Thread(t1,"B");
Thread t3=new Thread(t1,"C");
Thread t4=new Thread(t1,"D");
t1.start();
t2.start();
t3.start();
t4.start();
}
答案 0 :(得分:1)
您实际上并不需要wait()
和notify()
导致问题,而您似乎感到困惑。通常,wait()
和notify()
用于生产者/消费者问题(请参阅here),在您的代码中,您只是尝试控制对{{1}的访问权限来自您不需要int
和wait
的各种主题的变量。
您可以使用上面的Method1参考下面符合您目标的代码(运行4个线程并使用同步打印值),我基本上已经取出了notify
和wait()
来电。
notify()
此外,在您的代码中,使用public void add() {
synchronized(Testing.class){
i++;
System.out.println(i);
}
}
更有意义,因为您正在实例变量synchronized(this)
(来自i
对象)进行同步。
您可能需要注意的另一点是,对于您的任务(跨线程使用bbb
变量),您只需使用AtomicInteger API,您就可以避免显式同步< / strong>在您的代码中。
另外,我建议您重构代码,如下所示,因为将int
对象传递给thread1
非常混乱且难以理解(另外,确保Java命名约定,类名以大写字母开头):
thread2
答案 1 :(得分:0)
[[@ javaguy的回答是现场,但有一些东西丢失了所以我以为我会为后代做这件事。 ]
首先,您应该使用AtomicInteger
来完成所有手动同步。你的线程只需要做:
AtomicInteger i = new AtomicInteger();
...
for (int i = 0; i < 10000; i++) {
i.incrementAndGet();
}
由于AtomicInteger
包装volatile int
字段并且具有正确处理并发更新的逻辑,因此无需同步。
我用两种方法来做到这一点,每次运行代码时结果并不总是40000。我想知道什么是错的。
你永远不会达到40000的原因是你的手同步有竞争条件。在4个线程中的3个线程完成10000个线程后,最后一个线程运行将调用wait()
,但没有人通知它。如果您只是删除等待并通知,那么您的代码始终适用于我:
synchronized(bbb.class) {
// no wait/notify here
i++;
System.out.println(i);
}
其他几条评论:
static
锁定,请创建一个static
锁定对象:
private static final Object lockObject = new Object();
System.out.println(...)
,因为System.out
是PrintStream
,它本身是同步的。更好的模式是从i
中拍摄synchronized
的快照,然后将其打印到外面。synchronized
块内部执行 no IO。Bbb
或ThreadedCounter
。正如@javaguy所提到的,您正在将Thread
传递给另一个线程的构造函数。这可行,因为Thread
是Runnable
,但这是一个非常糟糕的模式,非常令人困惑。你应该这样做:
Runnable runnable = new Runnable() { ... };
Thread t1 = new Thread(runnable, "A");
Thread t2 = new Thread(runnable, "B");
...
最好创建一个类的实例并将其传递给每个线程,然后可以锁定它的instance
:
synchronized (this) {
i++;
}
...
final Bbb bbb = new Bbb();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0; i < 10000; i++){
bbb.add();
}
}
}, "A");
但是,AtomicInteger
和该包中的其他并发类是你的朋友。