我正在阅读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;
}
}
型
- 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;
}
}
型
- 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);
}
}
型
2条答案
按热度按时间2skhul331#
第一个问题的示例代码是:
字符串
在持有
this
上的锁时,不要调用nameList
对象的add
方法,这一点很重要,因为我们不知道add
的行为--例如,这可能需要很长时间。如果没有synchronised语句(即如果我们只有synchronised方法),我们需要写:
型
短语的解释:
避免同步调用其他对象的方法
“避免”做某事意味着不去做,所以我们应该“不同步调用其他对象的方法”。
“invocation”是一个调用,所以在上面的例子中,我们在
nameList
对象上 invokeadd
方法。示例方法中的代码在特定对象的上下文中运行,即
this
引用的对象,因此“其他对象的方法”是不是this
对象的对象上的方法。在上面的例子中,add
是对象nameList
上的方法。所以我们可以将其重新表述为“持有锁时不要调用其他对象的方法”(当然,除非你决定为了线程安全而需要这样做)。
对于第二个问题,假设
MsLunch
是这样实现的:型
这仍然是完全线程安全的,但是因为两个同步的部分都锁定在
MsLunch
示例中,所以调用inc1
的线程将被调用inc2
的线程阻塞。vfh0ocws2#
它或多或少就是“内在”这个词所描述的--即内在的、定性的。
实际上只有一个示例,并且一次只有 1 个线程可以访问它。
当另一个线程遇到 lock 时,JVM 将委托它是必须等待还是继续-在 Java 中,这被称为 ”blocking“。
它们是非常基本的概念,你只是简单的锁定一个类,或者示例,以防止并发修改。
换句话说,您可以 “微调” 锁定 ,以减少 “阻塞”。
尽管如此, 线程化 * 和 * 多线程化 * 的实际操作可能相当复杂。
我推荐您阅读 * O 'Reilly Media* 的 * Java Threads, 3rd Edition *。
......我真的听不懂他们在说什么,请举个简单的例子给我解释一下......"*
从本质上讲,他们的意思是,最好不要在一个锁中包含方法调用。
......我该怎么理解它呢?我想,它只是简单地提一下有两个对象被创建来管理各自的变量c1、c2,还有别的什么吗?......"*
这意味着,不需要 * 锁定 * 一个 MsLunch 示例,只需 * 锁定 * 一个关联的 Object。
这是整个 Java 框架中的常见做法。
请查看 * OpenJDK * 中的 BufferedReader 类源代码。