这是我的代码:
private int count = 0;
public synchronized void increment() {
count++;
}
public void doWork() throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 5; i++) {
increment();
System.out.println(count+" "+Thread.currentThread().getName());
}}});
Thread t2 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 5; i++) {
increment();
System.out.println(count+" "+Thread.currentThread().getName());
}}});
t1.start();
t2.start();
}
这是我的输出:
2 Thread-1
2 Thread-0
3 Thread-1
5 Thread-1
6 Thread-1
4 Thread-0
8 Thread-0
9 Thread-0
7 Thread-1
10 Thread-0
我的理解是increment
是synchronized
。因此,首先应increment
一个数字,然后释放lock
,然后将lock
提供给主题t1
或t2
。那么,它应该increment
一次一个数字,对吗?
但为什么我的代码incrementing
一次有两个或三个数字?我做错了什么(我是新手)?
答案 0 :(得分:5)
虽然count++;
确实已同步System.out.println(count+" "+Thread.currentThread().getName());
,但它可以访问count
变量。
即使您同步访问权限,它也无济于事,因为下一个方案仍然可能:
2
2
要解决此问题,您需要在同一部分中增加和打印。例如,您可以将System.out.println(count+" "+Thread.currentThread().getName());
放入increment
方法。
答案 1 :(得分:1)
increment
方法可以在increment
方法返回后在另一个线程上运行,但在为{{p>}检索count
之前
count+" "+Thread.currentThread().getName()
你可以,例如通过在一个同步块中修改和检索count
来解决此问题:
public synchronized int incrementAndGet() {
count++;
return count; // read access synchronized
}
for (int i = 0; i < 5; i++) {
System.out.println(incrementAndGet()+" "+Thread.currentThread().getName());
}
或使用the class in the standard library specifically designed for this purpose:
private final AtomicInteger counter = new AtomicInteger(0);
public void doWork() throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(counter.incrementAndGet() + " " + Thread.currentThread().getName());
}
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(counter.incrementAndGet() + " " + Thread.currentThread().getName());
}
}
});
t1.start();
t2.start();
}
当然,这并不一定导致数字1到10按顺序打印,只是没有多次检索数字。可能会发生以下输出:
2 Thread-0
3 Thread-0
4 Thread-0
1 Thread-1
5 Thread-0
6 Thread-1
7 Thread-0
8 Thread-1
9 Thread-1
10 Thread-1
答案 2 :(得分:0)
实际发生的是你的线程正在快照(可能是另一个词在这里更好)变量count
的当前值并显示它。您可以将其视为有一个数字为零的蓝色桶,并且Threads
都以相同的颜色和数字获得相同的桶。他们现在可以在这些桶上单独工作。
如果你想让它们在同一个桶上工作,你必须使它们成为原子的,例如:使用AtomicInteger
或volatile
或java并发包中的任何其他工具。
答案 3 :(得分:0)
解决方案1:由fabian提供。提供单一功能incrementAndGet()
。
解决方案2: synchronized
阻止而不是synchronized
方法(如果可能):
完整的代码就像:
private int count = 0;
private Object dummyObject = new Object();
public void increment() {
count++;
}
public int getCount() {
return count;
}
public void doWork() throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 5; i++) {
synchronized (dummyObject) {
increment();
System.out.println(count + " " + Thread.currentThread().getName());
}
}
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 5; i++) {
synchronized (dummyObject) {
increment();
System.out.println(count + " " + Thread.currentThread().getName());
}
}
}
});
t1.start();
t2.start();
}
答案 4 :(得分:0)
一种不使用synchronized
的替代解决方案。
由于您的使用案例很简单(只需启用计数器并打印值,AtomicInteger是更好的选择。
import java.util.concurrent.atomic.AtomicInteger;
public class TestCounter{
private AtomicInteger count = new AtomicInteger(0);
public void doWork() throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(""+Thread.currentThread().getName()+":"+count.incrementAndGet());
}}});
Thread t2 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(""+Thread.currentThread().getName()+":"+count.incrementAndGet());
}}});
t1.start();
t2.start();
}
public static void main(String args[]) throws Exception{
TestCounter tc = new TestCounter();
tc.doWork();
}
}
输出:
Thread-0:1
Thread-0:3
Thread-0:4
Thread-0:5
Thread-0:6
Thread-1:2
Thread-1:7
Thread-1:8
Thread-1:9
Thread-1:10
请参阅@fabian答案,了解为什么这些数字不是按顺序打印的。
如果您希望序列中的序列从1-10开始按升序排列,则不需要线程。