Java线程中的同步语句

lc8prwob  于 2023-11-15  发布在  Java
关注(0)|答案(2)|浏览(154)

我正在阅读the official tutorial上的主题,我试图理解内在的外观。
我现在明白了synchronized方法synchronized语句synchronized静态方法之间的基本区别。
在那篇文章的synchronized语句部分,给出了一个例子,他们说,
在本例中,addName方法需要同步对lastName和nameCount的更改,但也需要避免同步调用其他对象的方法。(从同步代码中删除其他对象的方法可能会产生有关Liveness一节中描述的问题。)如果没有synchronized语句,则必须有一个单独的非同步方法用于调用nameList. add。
我真的不明白他们说的是什么,请你举个简单的例子给我解释一下。
然后,他们说:
同步语句对于通过细粒度同步提高并发性也很有用。例如,假设类MsLunch有两个示例字段c1和c2,它们从不一起使用。这些字段的所有更新都必须同步,但是没有理由阻止c1的更新与c2的更新交错。这样做通过创建不必要的阻塞来减少并发。我们没有使用synchronized方法或与此相关的锁,而是创建两个对象来提供锁。
示例代码

public class MsLunch {
    private long c1 = 0;
    private long c2 = 0;
    private Object lock1 = new Object();
    private Object lock2 = new Object();

    public void inc1() {
        synchronized(lock1) {
            c1++;
        }
    }

    public void inc2() {
        synchronized(lock2) {
            c2++;
        }
    }
}

字符串
我应该如何理解它呢?我想,它只是简单地提到有两个对象被创建来管理各自的变量c1,c2,还有别的吗?

更新

在阅读了所有的评论和@tgdavies和@michael的回答之后,我非常感谢你们的支持,帮助我清楚地理解了这些概念。
对于第二个引号,我尝试进行比较以理解它。synchronized statement实际上比synchronized method
1.带有同步语句的MsLunch.java

public class MsLunch {
    private long c1 = 0;
    private long c2 = 0;
    private Object lock1 = new Object();
    private Object lock2 = new Object();

    public void inc1() {
        synchronized(lock1) {
            c1++;
        }
    }

    public void inc2() {
        synchronized(lock2) {
            c2++;
        }
    }

    public long getC1() {
        return c1;
    }

    public long getC2() {
        return c2;
    }
}

  1. MsLunch2.java使用同步方法
public class MsLunch2 {
    private long c1 = 0;
    private long c2 = 0;

    public synchronized void inc1() {
        c1++;
    }

    public synchronized void inc2() {
        c2++;
    }

    public long getC1() {
        return c1;
    }

    public long getC2() {
        return c2;
    }
}

  1. Test.java
public class Main {
    public static void main(String[] args) throws InterruptedException {
        // synchronized statements
//        MsLunch ms = new MsLunch();
        // synchronized method
        MsLunch2 ms = new MsLunch2();

        long start = System.currentTimeMillis();
        System.out.println(start);
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 100000000; i++) {
                ms.inc1();
            }
            System.out.println(ms.getC1());
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 100000000; i++) {
                ms.inc2();
            }
            System.out.println(ms.getC2());
        });

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

        t1.join();
        t2.join();

        long end = System.currentTimeMillis();
        System.out.println(end);
        System.out.println("Time = " + (end - start)/1000.0);
    }
}

2skhul33

2skhul331#

第一个问题的示例代码是:

public void addName(String name) {
    synchronized(this) {
        lastName = name;
        nameCount++;
    }
    nameList.add(name);
}

字符串
在持有this上的锁时,不要调用nameList对象的add方法,这一点很重要,因为我们不知道add的行为--例如,这可能需要很长时间。
如果没有synchronised语句(即如果我们只有synchronised方法),我们需要写:

public void addName(String name) {
    doSetLastName(name);
    nameList.add(name);
}

private synchronized doSetLastName(String name) {
        lastName = name;
        nameCount++;
}


短语的解释:
避免同步调用其他对象的方法
“避免”做某事意味着不去做,所以我们应该“不同步调用其他对象的方法”。
“invocation”是一个调用,所以在上面的例子中,我们在nameList对象上 invokeadd方法。
示例方法中的代码在特定对象的上下文中运行,即this引用的对象,因此“其他对象的方法”是不是this对象的对象上的方法。在上面的例子中,add是对象nameList上的方法。
所以我们可以将其重新表述为“持有锁时不要调用其他对象的方法”(当然,除非你决定为了线程安全而需要这样做)。
对于第二个问题,假设MsLunch是这样实现的:

public class MsLunch {
    private long c1 = 0;
    private long c2 = 0;

    public synchronized void inc1() {
            c1++;
    }

    public synchronized void inc2() {
            c2++;
    }
}


这仍然是完全线程安全的,但是因为两个同步的部分都锁定在MsLunch示例中,所以调用inc1的线程将被调用inc2的线程阻塞。

vfh0ocws

vfh0ocws2#

它或多或少就是“内在”这个词所描述的--即内在的、定性的。
实际上只有一个示例,并且一次只有 1 个线程可以访问它。
当另一个线程遇到 lock 时,JVM 将委托它是必须等待还是继续-在 Java 中,这被称为 blocking

  • "......我现在理解了synchronized方法synchronized语句synchronized静态方法之间的基本不同......"*

它们是非常基本的概念,你只是简单的锁定一个类,或者示例,以防止并发修改。
换句话说,您可以 “微调” 锁定 ,以减少 “阻塞”
尽管如此,
线程化 * 和 * 多线程化 * 的实际操作可能相当复杂。
我推荐您阅读 * O 'Reilly Media* 的 * Java Threads, 3rd Edition *。

  • "......在那篇文章的synchronized语句部分,举了一个例子,他们说......

......我真的听不懂他们在说什么,请举个简单的例子给我解释一下......"*
从本质上讲,他们的意思是,最好不要在一个锁中包含方法调用。

  • "......然后,他们说:......

......我该怎么理解它呢?我想,它只是简单地提一下有两个对象被创建来管理各自的变量c1、c2,还有别的什么吗?......"*
这意味着,不需要 * 锁定 * 一个 MsLunch 示例,只需 * 锁定 * 一个关联的 Object
这是整个 Java 框架中的常见做法。
请查看 * OpenJDK * 中的 BufferedReader 类源代码。


相关问题