Java 类成员的最小值和最大值

Java 类成员的最小值和最大值,java,multithreading,Java,Multithreading,我试图了解两个线程都是更新的classMember的可能值是多少,当我运行程序时,输出总是20,但我想了解为什么会发生这种情况,以及classMember的最大值是多少 public class TestClass { public int classMember = 0; private void updateCM() { for (int i = 0; i < 10; i++) { classMember++; }

我试图了解两个线程都是更新的
classMember
的可能值是多少,当我运行程序时,输出总是20,但我想了解为什么会发生这种情况,以及
classMember
的最大值是多少

public class TestClass {


public int classMember = 0;
    private void updateCM() {
        for (int i = 0; i < 10; i++) {
            classMember++;
        }
    }
public static void main(String[] args) {

    TestClass mainClass = new TestClass();
    Thread t1 = new Thread(mainClass::updateCM);
    Thread t2 = new Thread(mainClass::updateCM);

    t1.start();
    t2.start();

    while(t1.isAlive() || t2.isAlive()) {}

    System.out.println(mainClass.classMember);
}

}
公共类TestClass{
公共int类成员=0;
私有void updateecm(){
对于(int i=0;i<10;i++){
classMember++;
}
}
公共静态void main(字符串[]args){
TestClass mainClass=新的TestClass();
线程t1=新线程(mainClass::UpdateECM);
线程t2=新线程(mainClass::UpdateECM);
t1.start();
t2.start();
而(t1.isAlive()| | t2.isAlive()){}
System.out.println(mainClass.classMember);
}
}

我认为您需要阅读此Stackoverflow线程

由于在同一实例中更新同一变量,因此可能会出现同步问题。对于这种情况,合适的关键字应该是
volatile
。但是,即使将volatile设置为变量,这也是不够的,因为++实际上不是一个操作,而是三个操作,这使它成为非原子的

我也会引用这段话

虽然这会处理内存同步,但它不一定会保护您免受竞争条件的影响。认识到++实际上是3个操作也是很重要的:获取当前值,增加它,然后再次存储它。如果有多个线程试图执行此操作,则会出现线程争用情况,这可能会导致丢失++操作。 在这种情况下,您应该使用AtomicInteger类,它包装了一个volatile int字段。它提供了incrementAndGet()等方法,这些方法以线程安全的方式递增该字段


我想你需要读这篇文章

由于在同一实例中更新同一变量,因此可能会出现同步问题。对于这种情况,合适的关键字应该是
volatile
。但是,即使将volatile设置为变量,这也是不够的,因为++实际上不是一个操作,而是三个操作,这使它成为非原子的

我也会引用这段话

虽然这会处理内存同步,但它不一定会保护您免受竞争条件的影响。认识到++实际上是3个操作也是很重要的:获取当前值,增加它,然后再次存储它。如果有多个线程试图执行此操作,则会出现线程争用情况,这可能会导致丢失++操作。 在这种情况下,您应该使用AtomicInteger类,它包装了一个volatile int字段。它提供了incrementAndGet()等方法,这些方法以线程安全的方式递增该字段


最小值是初始化值,即0


关于最大值,尽管创建了两个线程实例,但两个实例都使用相同的TestClass实例,因此,将classMember变量增加2倍*10倍,得到的值为20。

最小值是初始化值,即0


关于最大值,尽管创建了两个线程实例,但两个实例都使用相同的TestClass实例,因此,将classMember变量增加2倍*10倍,得到的值为20。

增量不是原子操作,因此每次运行程序的结果都可能不同。在这种情况下,我认为,在处理器给第二个线程一段时间来执行其操作之前,第一个线程只是完成了变量值的递增。但是,例如,如果您启动两个线程,其中第一个线程将使变量的值递减10亿次,而第二个线程则相反-将其递增10亿次,您将得到非常意外的结果(当然,如果您不打算使此变量线程安全)。

递增不是原子操作,因此,每次运行程序时,结果可能会有所不同。在这种情况下,我认为,在处理器给第二个线程一段时间来执行其操作之前,第一个线程只是完成了变量值的递增。但是,例如,如果您启动两个线程,其中第一个线程将使变量的值减少10亿次,而第二个线程则相反-将变量的值增加10亿次,您将得到非常意外的结果(当然,如果您不打算使此变量线程安全)

现在该值设置为0

t1.start(); // 0-10
t2.start(); /10- 20
最后,
classMember
是20

现在该值设置为0

t1.start(); // 0-10
t2.start(); /10- 20

最后,
classMember
是20。

很可能是因为“运气”,或者“样本太小”,如果你愿意的话。JIT编译器还没有完成它的工作,所以无论运行什么都可能不是最优的,并且使用简单的技术


如果您将最大值从10更改为1000000,您将看到总数实际上并没有相加,并且在不同的运行中会产生不同的结果

最有可能是因为“运气”或“样本太少”,如果你愿意的话。JIT编译器还没有完成它的工作,所以无论运行什么都可能不是最优的,并且使用简单的技术


如果您将最大值从10更改为1000000,您将看到总数实际上并没有相加,并且在不同的运行中会产生不同的结果

所有介于10和20之间(包括10和20)的值都是可能的结果

在最坏的情况下,一个线程的每个增量(不是原子的,由读内存、增加内存、写内存组成)都与其他线程交错

可能的交错(为方便起见,省略了“增加”操作):

另一种可能的交错:

Thread1  Thread2
Read 0
         Read 0
         Write 1
         Read 1
         Write 2
         Read 2
         Write 3
         Read 3
         Write 4
         Read 4
         Write 5
         Read 5
         Write 6
         Read 6
         Write 7
         Read 7
         Write 8
         Read 8
         Write 9
         Read 9
         Write 10
Write 1
Read 1
Write 2
Read 2
Write 3
Read 3
Write 4
Read 4
Write 5
Read 5
Write 6
Read 6
Write 7
Read 7
Write 8
Read 8
Write 9
Read 9
Write 10

所有介于10和20(含10和20)之间的值都是可能的结果

在最坏的情况下,一个线程的每个增量(不是原子的,由读内存、增加内存、写内存组成)都与其他线程交错

可能的交错(省略了“增加”操作
Thread1  Thread2
Read 0
         Read 0
         Write 1
         Read 1
         Write 2
         Read 2
         Write 3
         Read 3
         Write 4
         Read 4
         Write 5
         Read 5
         Write 6
         Read 6
         Write 7
         Read 7
         Write 8
         Read 8
         Write 9
         Read 9
         Write 10
Write 1
Read 1
Write 2
Read 2
Write 3
Read 3
Write 4
Read 4
Write 5
Read 5
Write 6
Read 6
Write 7
Read 7
Write 8
Read 8
Write 9
Read 9
Write 10