Java线程一致性问题
Java Threads consistency issue
我有一个用例,我已经在一个小代码示例中复制了它:
package com.learning.thread;
public class ThreadInterupt {
public volatile int count;
public synchronized int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public synchronized void increment(){
count++;
}
public static void main(String[] args) throws InterruptedException {
ThreadInterupt local = new ThreadInterupt();
Thread firstThread = new Thread( new myThread(local));
Thread secondThread = new Thread( new myThread(local));
firstThread.start();
secondThread.start();
Thread.sleep(10);
firstThread.interrupt();
secondThread.interrupt();
}
}
class myThread implements Runnable{
static void threadMessage(String message) {
String threadName =
Thread.currentThread().getName();
System.out.format("%s: %s%n",
threadName,
message);
}
ThreadInterupt global;
public myThread(ThreadInterupt local){
global = local;
}
@Override
public void run() {
// TODO Auto-generated method stub
int i=0;
while(i<5){
if(Thread.interrupted()){
threadMessage("Got Killed");
break;
}
i++;
global.increment();
threadMessage(String.valueOf(global.getCount()));
}
}
}
我有两个线程共享一个变量 count ,它被定义为 volatile 。我在 ThreadInterupt class 中有两种方法,我在其中递增并获取计数变量,两者都定义为 synchronized 。我的问题是,虽然我希望第二个线程能够正确地获得增加的值,但它并没有这样做。我也在添加输出。
Thread-0: 1
Thread-1: 2
Thread-0: 3
Thread-0: 5
Thread-0: 6
Thread-0: 7
Thread-1: 4
Thread-1: 8
Thread-1: 9
Thread-1: 10
如您所见,线程 0 打印 7 后,线程 1 打印 4 。我在这里的假设是 Thread-1 进入同步方法 get 并被 thread-0 的增量方法阻塞,一旦它 thread-0 完成它打印出 get 。我不相信这一点。有人可以帮忙吗?
// 按照 codeBencher 的建议进行编辑,
这可能是因为延迟。当我将同步打印方法添加到 ThreadInterupt class 时,效果很好。
public synchronized void print(String threadName){
System.out.println(threadName+String.valueOf(count));
}
并在 运行 方法中
global.increment();
global.print(Thread.currentThread().getName());
这是一个竞争条件。 JVM 不保证有多少周期用于任一线程。所以 volatile 起作用了,因为您看不到相同的数字显示两次。但是跨两个线程执行命令的顺序可能与您预期的不同。它没有去 t-1 执行命令,现在 t-2 执行命令,现在又是 t-1,现在是 t-2。相反,它可能已经完成了 t-1、t-1、t-1、t-2、t-1、t-1、t-2、t-2、t-2、t-2、t-2。 ...等等等等。要强制它按照该顺序进行 t-1、t-2、t-1、t-2..,您将不得不使用 concurrent package 中的 类 的其他技术。
例如,一种常见的技术是使用 ConcurrentLatch 或 Future 来等待一个或多个其他线程完成。 CyclicBarrier 一个非常酷的可以用于倒计时(或向上计数)系统的系统是 CyclicBarrier,其中 t-1 和 t-2 可以各自递增一个 volatile 整数,然后等待 CyclicBarrier 让它们继续.
我有一个用例,我已经在一个小代码示例中复制了它:
package com.learning.thread;
public class ThreadInterupt {
public volatile int count;
public synchronized int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public synchronized void increment(){
count++;
}
public static void main(String[] args) throws InterruptedException {
ThreadInterupt local = new ThreadInterupt();
Thread firstThread = new Thread( new myThread(local));
Thread secondThread = new Thread( new myThread(local));
firstThread.start();
secondThread.start();
Thread.sleep(10);
firstThread.interrupt();
secondThread.interrupt();
}
}
class myThread implements Runnable{
static void threadMessage(String message) {
String threadName =
Thread.currentThread().getName();
System.out.format("%s: %s%n",
threadName,
message);
}
ThreadInterupt global;
public myThread(ThreadInterupt local){
global = local;
}
@Override
public void run() {
// TODO Auto-generated method stub
int i=0;
while(i<5){
if(Thread.interrupted()){
threadMessage("Got Killed");
break;
}
i++;
global.increment();
threadMessage(String.valueOf(global.getCount()));
}
}
}
我有两个线程共享一个变量 count ,它被定义为 volatile 。我在 ThreadInterupt class 中有两种方法,我在其中递增并获取计数变量,两者都定义为 synchronized 。我的问题是,虽然我希望第二个线程能够正确地获得增加的值,但它并没有这样做。我也在添加输出。
Thread-0: 1
Thread-1: 2
Thread-0: 3
Thread-0: 5
Thread-0: 6
Thread-0: 7
Thread-1: 4
Thread-1: 8
Thread-1: 9
Thread-1: 10
如您所见,线程 0 打印 7 后,线程 1 打印 4 。我在这里的假设是 Thread-1 进入同步方法 get 并被 thread-0 的增量方法阻塞,一旦它 thread-0 完成它打印出 get 。我不相信这一点。有人可以帮忙吗?
// 按照 codeBencher 的建议进行编辑,
这可能是因为延迟。当我将同步打印方法添加到 ThreadInterupt class 时,效果很好。
public synchronized void print(String threadName){
System.out.println(threadName+String.valueOf(count));
}
并在 运行 方法中
global.increment();
global.print(Thread.currentThread().getName());
这是一个竞争条件。 JVM 不保证有多少周期用于任一线程。所以 volatile 起作用了,因为您看不到相同的数字显示两次。但是跨两个线程执行命令的顺序可能与您预期的不同。它没有去 t-1 执行命令,现在 t-2 执行命令,现在又是 t-1,现在是 t-2。相反,它可能已经完成了 t-1、t-1、t-1、t-2、t-1、t-1、t-2、t-2、t-2、t-2、t-2。 ...等等等等。要强制它按照该顺序进行 t-1、t-2、t-1、t-2..,您将不得不使用 concurrent package 中的 类 的其他技术。
例如,一种常见的技术是使用 ConcurrentLatch 或 Future 来等待一个或多个其他线程完成。 CyclicBarrier 一个非常酷的可以用于倒计时(或向上计数)系统的系统是 CyclicBarrier,其中 t-1 和 t-2 可以各自递增一个 volatile 整数,然后等待 CyclicBarrier 让它们继续.