为什么 synchronized 不能正常工作?
Why is synchronized not working properly?
这是我的代码:
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
一次有两个或三个数字?我做错了什么吗(我是新手)?
虽然 count++;
确实是同步的 System.out.println(count+" "+Thread.currentThread().getName());
不是,但它访问 count
变量。
即使您同步访问,也无济于事,因为下一种情况仍有可能发生:
- 线程 1 增量
- 线程 2 增量
- 线程 1 打印值
2
- 线程 2 打印值
2
要解决此问题,您需要在同一同步部分中递增和打印。例如,您可以将 System.out.println(count+" "+Thread.currentThread().getName());
放入 increment
方法中。
实际发生的是您的线程正在快照(也许这里换个词更好)变量 count
的 当前 值并显示它。你可以把它想象成有一个数字为零的蓝色桶,并且 Threads
都得到相同颜色和数字的同一个桶。他们现在单独处理这些桶。
如果你想让它们在同一个桶上工作,你必须让它们成为原子,例如使用 AtomicInteger
或 volatile
或 java 并发包中的任何其他工具。
increment
方法可以在 increment
方法 returns 之后的另一个线程上 运行,但在 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
解决方案 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();
}
不使用 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 升序排列的一系列数字序列,则不需要线程。
这是我的代码:
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
一次有两个或三个数字?我做错了什么吗(我是新手)?
虽然 count++;
确实是同步的 System.out.println(count+" "+Thread.currentThread().getName());
不是,但它访问 count
变量。
即使您同步访问,也无济于事,因为下一种情况仍有可能发生:
- 线程 1 增量
- 线程 2 增量
- 线程 1 打印值
2
- 线程 2 打印值
2
要解决此问题,您需要在同一同步部分中递增和打印。例如,您可以将 System.out.println(count+" "+Thread.currentThread().getName());
放入 increment
方法中。
实际发生的是您的线程正在快照(也许这里换个词更好)变量 count
的 当前 值并显示它。你可以把它想象成有一个数字为零的蓝色桶,并且 Threads
都得到相同颜色和数字的同一个桶。他们现在单独处理这些桶。
如果你想让它们在同一个桶上工作,你必须让它们成为原子,例如使用 AtomicInteger
或 volatile
或 java 并发包中的任何其他工具。
increment
方法可以在 increment
方法 returns 之后的另一个线程上 运行,但在 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
解决方案 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();
}
不使用 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 升序排列的一系列数字序列,则不需要线程。