Java并发,同步。我在一个方法中递增两个变量,但它们是不同的。

huangapple go评论75阅读模式
英文:

Java Concurrency, synchronization. I increment two variables in one Method, but they are different

问题

现在我正在学习Java并发编程,并尝试编写一些涉及共享资源的线程示例。

在我的想法中,我想编写一个数据类,其中包含两个数字,并且我想创建两个线程。一个线程在一个方法中不断递增两个数字,另一个线程写入这些数字的值。

以下是代码。

package org.study.java.concurrency;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;

class SomeDataClass  {
    private final AtomicLong number1;
    private final AtomicLong number2;

    public SomeDataClass(Long number1, Long number2) {
        this.number1 = new AtomicLong(number1);
        this.number2 = new AtomicLong(number2);
    }

    public synchronized void NextStep() {
        synchronized (this) {
            this.setNumber1(getNumber1() + 1);
            this.setNumber2(getNumber2() + 1);
        }
        Thread.yield();
     }

    public synchronized Long getNumber1() {
        return number1.get();
    }

    public synchronized Long getNumber2() {
        return number2.get();
    }

    public synchronized void setNumber1(Long number1) {
        this.number1.set(number1);
    }

    public synchronized void setNumber2(Long number2) {
        this.number2.set(number2);
    }
}

class ProcessRunner implements Runnable {

    private final SomeDataClass dataClass;

    public ProcessRunner(SomeDataClass dataClass) {
        this.dataClass = dataClass;
        System.out.println("CameDataClass to ProcessRunner = " + dataClass);
        System.out.println("With values, number1 = " + dataClass.getNumber1() + ", number2 = " + dataClass.getNumber2());
    }

    @Override
    public void run() {
        while (true) {
            dataClass.NextStep();
        }
    }
}

class ProcessChecker implements Runnable {
    private final SomeDataClass dataClass;

    public ProcessChecker(SomeDataClass dataClass) {
        this.dataClass = dataClass;
        System.out.println("Came dataclass to process checker = " + dataClass);
        System.out.println("With values, number1 = " + dataClass.getNumber1() + ", number2 = " + dataClass.getNumber2());
    }

    @Override
    public void run() {
        try {
            while (true) {
                System.out.println("DataClass = " + dataClass + ",  CurrentNumber1 = " + dataClass.getNumber1() + ", CurrentNumber2 = " + dataClass.getNumber2());
                TimeUnit.SECONDS.sleep(2);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ConcurrencyExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        SomeDataClass dataClass = new SomeDataClass(1L, 1L);
        executorService.submit(new ProcessRunner(dataClass));
        executorService.submit(new ProcessChecker(dataClass));

        executorService.shutdown();
    }
}

这段代码的结果是:

CameDataClass to ProcessRunner = org.study.java.concurrency.SomeDataClass@266474c2
With values, number1 = 1, number2 = 1
Came dataclass to process checker = org.study.java.concurrency.SomeDataClass@266474c2
With values, number1 = 177, number2 = 180
DataClass = org.study.java.concurrency.SomeDataClass@266474c2, CurrentNumber1 = 1024, CurrentNumber2 = 1026
DataClass = org.study.java.concurrency.SomeDataClass@266474c2, CurrentNumber1 = 2787500, CurrentNumber2 = 2787531
DataClass = org.study.java.concurrency.SomeDataClass@266474c2, CurrentNumber1 = 5828168, CurrentNumber2 = 5828509
DataClass = org.study.java.concurrency.SomeDataClass@266474c2, CurrentNumber1 = 8834895, CurrentNumber2 = 8837607
Process finished with exit code 130 (interrupted by signal 2: SIGINT)
看,CurrentNumber1和CurrentNumber2是不同的,但我预期它们应该是相同的。
问题是数字是不同的。我不明白为什么它们不同,以及我应该在代码中进行哪些更改以使它们相等?
英文:

Now I'm learning Java concurrency, and I've tried to write some example with threads that uses same resources.

In my idea, I wanted to write some data class, that contains two numbers and I wanted to create two threads. One thread is constantly increment two numbers in one method, and another thread writes the values of numbers.

Code is below.

package org.study.java.concurrency;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
class SomeDataClass  {
private final AtomicLong number1;
private final AtomicLong number2;
public SomeDataClass(Long number1, Long number2) {
this.number1 = new AtomicLong(number1);
this.number2 = new AtomicLong(number2);
}
public synchronized void NextStep() {
synchronized (this) {
this.setNumber1(getNumber1() + 1);
this.setNumber2(getNumber2() + 1);
}
Thread.yield();
}
public synchronized Long getNumber1() {
return number1.get();
}
public synchronized Long getNumber2() {
return number2.get();
}
public synchronized void setNumber1(Long number1) {
this.number1.set(number1);
}
public synchronized void setNumber2(Long number2) {
this.number2.set(number2);
}
}
class ProcessRunner implements Runnable {
private final SomeDataClass dataClass;
public ProcessRunner(SomeDataClass dataClass) {
this.dataClass = dataClass;
System.out.println("CameDataClass to ProcessRunner = " + dataClass);
System.out.println("With values, number1 = " + dataClass.getNumber1() + ", number2 = " + dataClass.getNumber2());
}
@Override
public void run() {
while (true) {
dataClass.NextStep();
}
}
}
class ProcessChecker implements Runnable {
private final SomeDataClass dataClass;
public ProcessChecker(SomeDataClass dataClass) {
this.dataClass = dataClass;
System.out.println("Came dataclass to process checker = " + dataClass);
System.out.println("With values, number1 = " + dataClass.getNumber1() + ", number2 = " + dataClass.getNumber2());
}
@Override
public void run() {
try {
while (true) {
System.out.println("DataClass = " + dataClass + ",  CurrentNumber1 = " + dataClass.getNumber1() + ", CurrentNumber2 = " + dataClass.getNumber2());
TimeUnit.SECONDS.sleep(2);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ConcurrencyExample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
SomeDataClass dataClass = new SomeDataClass(1L, 1L);
executorService.submit(new ProcessRunner(dataClass));
executorService.submit(new ProcessChecker(dataClass));
executorService.shutdown();
}
}

The result of this code is

And the result is like
CameDataClass to ProcessRunner = org.study.java.concurrency.SomeDataClass@266474c2
With values, number1 = 1, number2 = 1
Came dataclass to process checker = org.study.java.concurrency.SomeDataClass@266474c2
With values, number1 = 177, number2 = 180
DataClass = org.study.java.concurrency.SomeDataClass@266474c2, CurrentNumber1 = 1024, CurrentNumber2 = 1026
DataClass = org.study.java.concurrency.SomeDataClass@266474c2, CurrentNumber1 = 2787500, CurrentNumber2 = 2787531
DataClass = org.study.java.concurrency.SomeDataClass@266474c2, CurrentNumber1 = 5828168, CurrentNumber2 = 5828509
DataClass = org.study.java.concurrency.SomeDataClass@266474c2, CurrentNumber1 = 8834895, CurrentNumber2 = 8837607
Process finished with exit code 130 (interrupted by signal 2: SIGINT)
Look, the CurrentNumber1 and CurrentNumber2 is different, but I expected that they would be the same.

Look, the CurrentNumber1 and CurrentNumber2 is different, but I expected that they would be the same.

The problem is that the numbers are different. I don't understand why they are different and what I should change in code to make them equal?

答案1

得分: 0

System.out.println("DataClass = " + dataClass + ", CurrentNumber1 = " + dataClass.getNumber1() + ", CurrentNumber2 = " + dataClass.getNumber2());

You are calling `dataClass.getNumber1()` and `dataClass.getNumber2()` as part of println. But there is no guarantee both calls will be made one after another. What is happening in this case is the updater thread is updating values between these two calls.

1. `getNumber1()`
2. ..ProcessRunner runs many times...
3. `getNumber2()`

Synchronizing on the `dataClass` object will produce the same value for `number1` and `number2` as the entire print statement will be executing atomically.

synchronized (dataClass) {
    System.out.println("DataClass = " + dataClass + ", CurrentNumber1 = " + dataClass.getNumber1() + ", CurrentNumber2 = " + dataClass.getNumber2());
}

Or, add a method to print the values in the `SomeDataClass` class and call it from the `ProcessChecker`.

public synchronized void printNumbers() {
    System.out.println("CurrentNumber1 = " + getNumber1() + ", CurrentNumber2 = " + getNumber2());
}
英文:
System.out.println("DataClass = " + dataClass + ",  CurrentNumber1 = " + dataClass.getNumber1() + ", CurrentNumber2 = " + dataClass.getNumber2());

You are calling dataClass.getNumber1() and dataClass.getNumber2() as part of println. But there is no guarantee both calls will be made one after another. What is happening in this case is the updater thread is updating values between these two calls.

  1. getNumber1()
  2. ..ProcessRunner runs many times...
  3. getNumber2()

Synchronizing on the dataClass object will produce the same value for number1 and number2 as the entire print statement will be executing atomically.

synchronized (dataClass) {
System.out.println("DataClass = " + dataClass + ",  CurrentNumber1 = " + dataClass.getNumber1() + ", CurrentNumber2 = " + dataClass.getNumber2());
}

Or, add a method to print the values in the SomeDataClass class and call it from the ProcessChecker.

public synchronized void printNumbers() {
System.out.println("CurrentNumber1 = " + getNumber1() + ", CurrentNumber2 = " + getNumber2());
}

huangapple
  • 本文由 发表于 2020年10月4日 13:56:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/64191601.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定