在“Counter”示例 class 中,线程干扰实际上是如何发生的?
How does thread interference actually happens in the `Counter` example class?
我正在尝试学习线程干扰的概念,并且在 Java Oracle 教程中遇到了以下示例:
class Counter {
private int c = 0;
public void increment() {
c++;
}
public void decrement() {
c--;
}
public int value() {
return c;
}
}
Oracle 教程提到,如果有两个线程尝试访问变量 c
,则可能会导致线程干扰,其中一个线程所做的更改不会被另一个线程看到。
但是,它没有提供任何代码来实际解释这个概念。有人可以提供一个基于 Counter
class 的代码示例来演示线程干扰实际上是如何发生的吗?
比起代码,我更喜欢解释会发生什么。假设 2 个线程 A 和 B 正在访问 相同的计数器对象 ,A 调用 increment 和 B 调用 递减 。这些操作中的任何一个都至少包含 3 个步骤。
- 从内存中读取 C
- 递增或递减C
- 写回C到内存
当 A 和 B 尝试同时递增和递减时,一个线程可能从内存中读取 C(步骤 1),而另一个线程在 步骤 2。在这种情况下,如果 c 的初始值为 5,首先线程 A 读取并将其递增到 6。然后线程 B 读取并将其递减到 4。请记住,B 之前进行了这些更改A写完c回内存。由于步骤重叠,一个线程所做的更改对另一个线程不可见,导致 c 的最终值为 6 或 4.但实际上我们期望的是5。
这是两个线程相互干扰的例子。为了避免这种情况,我们使用线程同步。
只是 运行 代码很多次,其中一次幸运的是您将能够看到正在运行的线程干扰。
在这种情况下很少观察到,因为它是一个小程序,而且不多 happening.If 你制作多个递增和递减线程,线程干扰将更容易观察到。
class Counter {
private int c = 0;
public void increment() {c++;}
public void decrement() {c--;}
public int value() {
return c;
}
public static void main(String[] args) {
Counter x = new Counter();
Runnable r1 = new Runnable() {
@Override
public void run() {
x.increment();
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
x.decrement();
}
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
System.out.println(x.c);
}
}
编辑:我决定添加多线程案例,我无法抗拒
编辑 2:这是第二个 edit.The 多线程案例,因为它产生的问题超出了这个问题的范围,我决定删除 it.previously 我正在制作一个线程数组 运行ning them.Instead 最好显示一个执行大量递增的线程和另一个执行大量递减的线程。
我已经使用 Thread.Sleep() 使主线程进入睡眠状态,这将确保在两个线程都完成对它的操作后打印 c。
class Counter {
private int c = 0;
public void increment() {
for (int i = 0; i < 10000; i++) {
c++;
}
}
public void decrement() {
for (int i = 0; i < 5000; i++) {
c--;
}
}
public int value() {
return c;
}
public static void main(String[] args) {
Counter x = new Counter();
Runnable r1 = new Runnable() {
@Override
public void run() {
x.increment();
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
x.decrement();
}
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(!(t1.isAlive() && t2.isAlive()))
System.out.println(x.c);//expected answer 5000
}
}
注意:同步 Increment/Decrement 方法给自己正确的 answer.Try。
创建多个线程并从这些线程调用 increment(), decrement()
和 value()
。
示例代码如下:
class Counter {
private int c = 0;
public void increment() {
c++;
}
public void decrement() {
c--;
}
public int value() {
return c;
}
public static void main(String args[]){
Counter c = new Counter();
for ( int i=0; i<3; i++){
Thread t = new Thread(new MyRunnable(c));
t.start();
}
}
}
class MyRunnable implements Runnable{
Counter counter;
public MyRunnable(Counter c){
counter = c;
}
public void run(){
counter.increment();
System.out.println("Counter value after increment:"+counter.value()+" from thread:"+ Thread.currentThread().getName());
counter.decrement();
System.out.println("Counter value after decrement:"+counter.value()+" from thread:"+ Thread.currentThread().getName());
}
}
输出:(每个 运行 的输出会有所不同)
Counter value after increment:1 from thread:Thread-0
Counter value after decrement:2 from thread:Thread-0
Counter value after increment:2 from thread:Thread-2
Counter value after decrement:1 from thread:Thread-2
Counter value after increment:3 from thread:Thread-1
Counter value after decrement:0 from thread:Thread-1
现在从输出中,你可以理解线程干扰。要解决此问题,您可以使用 AtomicInteger.
查看下面post了解更多详情:
我正在尝试学习线程干扰的概念,并且在 Java Oracle 教程中遇到了以下示例:
class Counter {
private int c = 0;
public void increment() {
c++;
}
public void decrement() {
c--;
}
public int value() {
return c;
}
}
Oracle 教程提到,如果有两个线程尝试访问变量 c
,则可能会导致线程干扰,其中一个线程所做的更改不会被另一个线程看到。
但是,它没有提供任何代码来实际解释这个概念。有人可以提供一个基于 Counter
class 的代码示例来演示线程干扰实际上是如何发生的吗?
比起代码,我更喜欢解释会发生什么。假设 2 个线程 A 和 B 正在访问 相同的计数器对象 ,A 调用 increment 和 B 调用 递减 。这些操作中的任何一个都至少包含 3 个步骤。
- 从内存中读取 C
- 递增或递减C
- 写回C到内存
当 A 和 B 尝试同时递增和递减时,一个线程可能从内存中读取 C(步骤 1),而另一个线程在 步骤 2。在这种情况下,如果 c 的初始值为 5,首先线程 A 读取并将其递增到 6。然后线程 B 读取并将其递减到 4。请记住,B 之前进行了这些更改A写完c回内存。由于步骤重叠,一个线程所做的更改对另一个线程不可见,导致 c 的最终值为 6 或 4.但实际上我们期望的是5。
这是两个线程相互干扰的例子。为了避免这种情况,我们使用线程同步。
只是 运行 代码很多次,其中一次幸运的是您将能够看到正在运行的线程干扰。
在这种情况下很少观察到,因为它是一个小程序,而且不多 happening.If 你制作多个递增和递减线程,线程干扰将更容易观察到。
class Counter {
private int c = 0;
public void increment() {c++;}
public void decrement() {c--;}
public int value() {
return c;
}
public static void main(String[] args) {
Counter x = new Counter();
Runnable r1 = new Runnable() {
@Override
public void run() {
x.increment();
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
x.decrement();
}
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
System.out.println(x.c);
}
}
编辑:我决定添加多线程案例,我无法抗拒
编辑 2:这是第二个 edit.The 多线程案例,因为它产生的问题超出了这个问题的范围,我决定删除 it.previously 我正在制作一个线程数组 运行ning them.Instead 最好显示一个执行大量递增的线程和另一个执行大量递减的线程。
我已经使用 Thread.Sleep() 使主线程进入睡眠状态,这将确保在两个线程都完成对它的操作后打印 c。
class Counter {
private int c = 0;
public void increment() {
for (int i = 0; i < 10000; i++) {
c++;
}
}
public void decrement() {
for (int i = 0; i < 5000; i++) {
c--;
}
}
public int value() {
return c;
}
public static void main(String[] args) {
Counter x = new Counter();
Runnable r1 = new Runnable() {
@Override
public void run() {
x.increment();
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
x.decrement();
}
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(!(t1.isAlive() && t2.isAlive()))
System.out.println(x.c);//expected answer 5000
}
}
注意:同步 Increment/Decrement 方法给自己正确的 answer.Try。
创建多个线程并从这些线程调用 increment(), decrement()
和 value()
。
示例代码如下:
class Counter {
private int c = 0;
public void increment() {
c++;
}
public void decrement() {
c--;
}
public int value() {
return c;
}
public static void main(String args[]){
Counter c = new Counter();
for ( int i=0; i<3; i++){
Thread t = new Thread(new MyRunnable(c));
t.start();
}
}
}
class MyRunnable implements Runnable{
Counter counter;
public MyRunnable(Counter c){
counter = c;
}
public void run(){
counter.increment();
System.out.println("Counter value after increment:"+counter.value()+" from thread:"+ Thread.currentThread().getName());
counter.decrement();
System.out.println("Counter value after decrement:"+counter.value()+" from thread:"+ Thread.currentThread().getName());
}
}
输出:(每个 运行 的输出会有所不同)
Counter value after increment:1 from thread:Thread-0
Counter value after decrement:2 from thread:Thread-0
Counter value after increment:2 from thread:Thread-2
Counter value after decrement:1 from thread:Thread-2
Counter value after increment:3 from thread:Thread-1
Counter value after decrement:0 from thread:Thread-1
现在从输出中,你可以理解线程干扰。要解决此问题,您可以使用 AtomicInteger.
查看下面post了解更多详情: