Java 并发性,对象可见性

Java 并发性,对象可见性,java,concurrency,multithreading,volatile,Java,Concurrency,Multithreading,Volatile,我试图弄清楚下面的代码是否存在任何潜在的并发性问题。具体而言,可见性问题与可变变量有关。Volatile被定义为:此变量的值将永远不会被本地线程缓存:所有读写操作将直接进入“主内存” publicstaticvoidmain(字符串[]args) { 测试=新测试(); //这将始终是单线程的 ExecutorService ex=Executors.newSingleThreadExecutor(); 对于(inti=0;i如果您的ExecutorService是单线程的,那么就没有共享状态,

我试图弄清楚下面的代码是否存在任何潜在的并发性问题。具体而言,可见性问题与可变变量有关。Volatile被定义为:此变量的值将永远不会被本地线程缓存:所有读写操作将直接进入“主内存”

publicstaticvoidmain(字符串[]args)
{
测试=新测试();
//这将始终是单线程的
ExecutorService ex=Executors.newSingleThreadExecutor();

对于(inti=0;i如果您的ExecutorService是单线程的,那么就没有共享状态,所以我不认为这会有任何问题

然而,将
测试的新实例传递给
execute()
的每个调用不是更有意义吗

for (int i=0; i<10; ++i)
    ex.execute(new Test());

for(int i=0;i只要它只是一个线程,就没有必要使它变为易失性线程。如果要使用多个线程,不仅要使用易失性线程,还要进行同步。增加一个数字不是一个原子操作-这是一个常见的误解

public void run() {
    synchronize (this) {
        if (this.state != -1)
            this.state++;
    }
}
除了使用同步,您还可以使用(如果您以前不需要if)


你的代码,特别是这一点

            if (this.state != -1)
                    this.state++;
将需要对状态值进行原子测试,然后在并发上下文中对状态进行增量。因此,即使您的变量不稳定且涉及多个线程,也会出现并发问题

但是您的设计是基于这样一种断言,即始终只有一个测试实例,并且该单个实例仅被授予一个(相同)线程(但请注意,该单个实例实际上是主线程和执行线程之间的共享状态)


我认为您需要使这些假设更加明确(例如,在代码中使用ThreadLocal和ThreadLocal.get())。这是为了防止将来出现错误(当其他开发人员可能不小心违反设计假设时),并且,避免对内部实现进行假设,在某些实现中,内部实现可能只提供单线程执行器(即,在每次调用execute(runnable)时顺序执行,而不一定是同一线程).

在此特定代码中,状态为非易失性是完全可以的,因为只有一个线程,并且只有该线程访问该字段。禁用在您拥有的唯一线程中缓存该字段的值只会导致性能下降

但是,如果希望在运行循环的主线程中使用state值,则必须使该字段不稳定:

    for (int i=0; i<10; ++i) {
            ex.execute(test);
            System.out.println(test.getState());
    }

对于(int i=0;i最初,我是这样想的:

如果任务总是由 同一条线,就不会有 问题。但是由产生的异常
newSingleThreadExecutor()
可以创建 新的线程替换那些 因为任何原因都会被杀,没有 保证何时更换 将创建线程,或者创建哪个线程 我会创造它

如果一个线程执行一些写操作,那么 在新线程上调用
start()
,这些 写入操作将对新用户可见 线程。但不能保证 这条规则适用于这种情况


但是不可修复是正确的:创建正确的
ExecutorService
而没有足够的屏障来确保可见性实际上是不可能的。我忘记了检测另一个线程的死亡是一种与同步的关系。用于空闲工作线程的阻塞机制也需要屏障。

是的,它是FE,即使执行器在中间替换它的线程,线程启动/终止也是同步点。

一个简单的例子:

static int state;
static public void main(String... args) {
    state = 0;                   // (1)
    Thread t = new Thread() {
        public void run() {
            state = state + 1;   // (2) 
        }
    };
    t.start();
    t.join();
    System.out.println(state);  // (3)
}
保证(1)、(2)、(3)有序且行为符合预期


对于单线程执行器,“任务保证按顺序执行”,它必须在启动下一个任务之前以某种方式检测一个任务的完成情况,这必然会正确地同步不同的
run()
的从何处获得该定义。听起来像是1.5之前的JMM定义(无法实现)。重要的是要认识到,当线程完成
Test.run()
时,线程不会终止,并且任何关于线程在终止前刷新到主存的值的保证都不适用。调用
Test.run()的
run()
方法线程
只是一个循环,它会阻塞,直到它接收到要执行的新任务。当该任务从其
run()
方法返回时,线程会阻塞,直到下一个任务;它不会终止(从而刷新其状态)。此q/a的投票者是错误的。由于线程开始/结束的“发生之前”关系,代码是安全的。请参阅我下面的答复。您是否知道,例如,在10个线程总共调用了1000次this.state++之后,this.state的值可能小于1000?我不确定您是否只是没有询问此并发问题,或者没有我没有意识到这一点。这毫无意义,问题的关键是使用了同一个对象。我只是问了一个简单的问题,其他线程是否会在单线程模式下看到状态更改,您回答了这个问题。但是,我不明白为什么要使用volatile和synchronize。看起来对我来说,同步就足够了。我只是想让你(和其他人在这个问题上绊倒)意识到在多线程环境中,使用volatile递增整数是不够的。您始终需要volatile和synchronized这两种方式才能实现线程安全。我很清楚volatile通常不够的原因:由于++的非原子性,线程的某些增量可能不会因竞争条件。但是为什么没有volatile的同步是不够的呢
    for (int i=0; i<10; ++i) {
            ex.execute(test);
            System.out.println(test.getState());
    }
static int state;
static public void main(String... args) {
    state = 0;                   // (1)
    Thread t = new Thread() {
        public void run() {
            state = state + 1;   // (2) 
        }
    };
    t.start();
    t.join();
    System.out.println(state);  // (3)
}