java - 如何在java中使用内部锁?

标签 java multithreading concurrency synchronization

我在看官方Java Tutorial由 Oracle 提供,我想将我的知识付诸实践。

我想看Thread Interference在行动中并使用 Intrinsic Locks and Synchronization 解决它.所以我创建了一个名为 Counter 的类:

  • 两个都初始化为 0 的字段。
  • 递增和递减它们的值的方法。
  • 打印值的方法。

.

public class Apple {
    public static void main(String[] args) {
        Counter myCounter = new Counter();
        Thread a = new Thread(myCounter);
        Thread b = new Thread(myCounter);
     
        a.start();
        b.start();
    }
}

class Counter implements Runnable {
    public int a = 0;
    public int b = 0;

    void incA() {
        ++a;
    }

    void decA() {
        --a;
    }

    void incB() {
        ++b;
    }

    void decB() {
        --b;
    }

    void printValues() {
        System.out.println("a: " + a + " | b: " + b);
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            incA();
            decA();
            incB();
            decB();
            printValues();
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

首先,我在不使用内在锁或同步的情况下运行我的程序,输出是我所期望的,线程干扰。

...
a: 0 | b: 0
a: 0 | b: 1
a: 0 | b: 0
a: 1 | b: 0
a: 0 | b: 0
...

现在我想使用内部锁来解决这个问题,这样当一个线程递增或递减 a 时,另一个线程可以同时更改 b,而不是使用会阻止这种情况的同步方法。

所以我添加了两个新字段(锁)和使用内部锁的同步块(synchronized block)。这是新代码:

class Counter implements Runnable {
    public int a = 0;
    public int b = 0;
    Object lock1 = new Object();
    Object lock2 = new Object();

    void incA() {
        synchronized (lock1) {
            ++a;
        }
    }

    void decA() {
        synchronized (lock1) {
            --a;
        }
    }

    void incB() {
        synchronized (lock2) {
            ++b;
        }
    }

    void decB() {
        synchronized (lock2) {
            --b;
        }
    }

    void printValues() {
        System.out.println("a: " + a + " | b: " + b);
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            incA();
            decA();
            incB();
            decB();
            printValues();
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

我没有对 main 方法做任何更改,所以我没有包含它。但是我将这段代码运行了三次,这是最后一次尝试的输出:

...
a: 0 | b: 0
a: 0 | b: 0
a: 0 | b: 1
a: 0 | b: 0

我做错了什么?代码应该是什么样子?

我只想要一个使用内部锁的简单示例代码。

最佳答案

printValues() 读取并打印值,同时另一个线程递增或递减它们:它不是同步的。即使是,它也可以在调用另一个线程的 incB()decB() 之间读取和打印 b

所以你可以拥有

  • 线程 1 递增 b --> b = 1
  • 线程 2 读取并打印 b --> b 打印为 1
  • 线程 1 递减 b --> b = 0

如果没有人认为 b 不同于 0,那么 incB();decB() 应该是单个原子操作,将这两个调用放在一个同步块(synchronized block)中,并且b 的读取也应该放入同步块(synchronized block)中,使用相同的锁:

class Counter implements Runnable {
    private int a = 0;
    private int b = 0;
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();

    private void incA() {
        ++a;
    }

    private void decA() {
        --a;
    }

    private void incB() {
        ++b;
    }

    private void decB() {
        --b;
    }

    private void printValues() {
        System.out.println("a: " + a + " | b: " + b);
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            synchronized (lock1) {
                incA();
                decA();
            }

            synchronized (lock2) {
                incB();
                decB();
            }

            synchronized (lock1) {
                synchronized (lock2) {
                    printValues();
                }
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

关于java - 如何在java中使用内部锁?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34691903/

相关文章:

python - 终止线程: Python

c# - ConcurrentStack - 从底部删除或等效功能

Java/ Spring MVC : provide request context to child threads

java - Java 中初始化器与构造器的使用

java - 用于通过 IIS 为 java 应用程序提供服务的轻量级 servlet 引擎

java - OpenCV : Add/merge/combine 'only part' of Image A to Image B

java - 线程在android中是如何工作的

java - 为每个线程创建一个非线程安全对象并使用 happens-before 保证

android - 使用 Sprite 数组列表时出现并发错误

java - 转换为从不同类加载器加载的类时出现 CastCastException