如果我在同一个类中有2个同步方法,但每个方法访问不同的变量,那么2个线程可以同时访问这两个方法吗?锁是否发生在对象上,或者它是否与synchronized方法中的变量一样具体?
示例:
class X {
private int a;
private int b;
public synchronized void addA(){
a++;
}
public synchronized void addB(){
b++;
}
}
2个线程可以同时访问同一个执行x.addA(
)和x.addB()
的X类实例吗?
答案 0 :(得分:168)
如果您将方法声明为同步(正如您通过键入public synchronized void addA()
所做的那样),则在整个对象上进行同步,因此从同一对象访问不同变量的两个线程将无论如何阻止对方。
如果您希望一次仅同步一个变量,那么两个线程在访问不同变量时不会相互阻塞,您可以在synchronized ()
块中单独同步它们。如果a
和b
是对象引用,您将使用:
public void addA() {
synchronized( a ) {
a++;
}
}
public void addB() {
synchronized( b ) {
b++;
}
}
但由于他们是原始人,你不能这样做。
我建议你改用AtomicInteger:
import java.util.concurrent.atomic.AtomicInteger;
class X {
AtomicInteger a;
AtomicInteger b;
public void addA(){
a.incrementAndGet();
}
public void addB(){
b.incrementAndGet();
}
}
答案 1 :(得分:58)
对方法声明进行同步是这样的语法糖:
public void addA() {
synchronized (this) {
a++;
}
}
在静态方法上,它是语法糖:
ClassA {
public static void addA() {
synchronized(ClassA.class) {
a++;
}
}
我认为如果Java设计人员知道现在对同步有什么了解,他们就不会添加语法糖,因为它往往会导致并发性的不良实现。
答案 2 :(得分:13)
访问的锁是在对象上,而不是在方法上。在该方法中访问哪些变量是无关紧要的。
向方法添加“synchronized”意味着运行代码的线程必须在继续之前获取对象的锁定。添加“静态同步”意味着运行代码的线程必须在继续之前获取类对象的锁定。或者,您可以将代码包装在这样的块中:
public void addA() {
synchronized(this) {
a++;
}
}
以便您可以指定必须获取其锁定的对象。
如果您想避免锁定包含对象,可以选择:
答案 3 :(得分:13)
From the Java SE essentials on synchronized methods:
首先,对同一对象的两个同步方法的调用不可能进行交错。当一个线程正在为对象执行同步方法时,所有其他线程都会调用同一对象的同步方法阻塞(暂停执行),直到第一个线程完成对象为止。
来自Java SE essentials on synchronized blocks:
同步语句对于通过细粒度同步提高并发性也很有用。例如,假设类MsLunch有两个从不一起使用的实例字段c1和c2。必须同步这些字段的所有更新,但没有理由阻止c1的更新与c2 的更新交错 - 这样做会通过创建不必要的阻塞来减少并发性。 我们不是使用同步方法或使用与此相关联的锁,而是仅创建两个对象以提供锁定。
(强调我的。)
你有两个变量no-interleaved。因此,您希望同时访问来自不同线程的每个线程。你需要定义锁不在对象类本身上,而是在类Object上定义如下(例如来自第二个Oracle链接):
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++;
}
}
}
答案 4 :(得分:4)
来自oracle文档link
使方法同步有两个影响:
首先,对同一对象的两个同步方法的调用不可能进行交错。当一个线程正在为对象执行同步方法时,所有其他线程都会调用同一对象的同步方法阻塞(暂停执行),直到第一个线程完成对象为止。
其次,当synchronized方法退出时,它会自动与同一对象的同步方法的任何后续调用建立一个before-before关系。这可以保证对所有线程都可以看到对象状态的更改
查看本文档page以了解内部锁和锁定行为。
这将回答您的问题:在同一个对象x上,当其中一个同步方法执行时,您无法同时调用x.addA()和x.addB()进展。 强>
答案 5 :(得分:2)
您可以执行以下操作。在这种情况下,您使用a和b上的锁来同步而不是锁定“this”。我们不能使用int,因为原始值没有锁,所以我们使用Integer。
class x{
private Integer a;
private Integer b;
public void addA(){
synchronized(a) {
a++;
}
}
public synchronized void addB(){
synchronized(b) {
b++;
}
}
}
答案 6 :(得分:2)
如果您有一些未同步的方法,并且正在访问和更改实例变量。在您的示例中:
private int a;
private int b;
当其他线程位于同一对象的synchronized方法中并且可以更改实例变量时,任意数量的线程都可以同时访问这些非同步方法。 例如: -
public void changeState() {
a++;
b++;
}
您需要避免非同步方法正在访问实例变量并更改它的情况,否则无法使用同步方法。
在以下场景中: -
class X {
private int a;
private int b;
public synchronized void addA(){
a++;
}
public synchronized void addB(){
b++;
}
public void changeState() {
a++;
b++;
}
}
只有一个线程可以在addA或addB方法中,但同时任意数量的线程都可以进入changeState方法。没有两个线程可以同时输入addA和addB(因为对象级别锁定)但同时任意数量的线程都可以进入changeState。
答案 7 :(得分:1)
这个例子(尽管不是很好)可以提供对锁定机制的更多了解。如果 incrementA 同步,并且 incrementB 未同步,那么 incrementB 将是尽快执行,但如果 incrementB 也同步那么它必须'等待' incrementA 完成,然后 incrementB >可以做好自己的工作。
两个方法都被调用到单个实例 - 对象,在这个例子中它是: job ,而'竞争'线程是 aThread 和 main 。
尝试 incrementB 中的“同步”,如果没有它,您会看到不同的结果。如果 incrementB 是“同步< / strong>'然后它必须等待 incrementA ()才能完成。每个变体运行几次。
200dp
答案 8 :(得分:1)
是的,它会阻止另一个方法,因为synchronized方法适用于指向的 WHOLE 类对象....但无论如何它会阻止其他线程执行 ONLY 在它进入的任何方法addA或addB中执行求和时,因为当它完成时...一个线程将自由对象,另一个线程将访问另一个方法,等等完美地工作。 / p>
我的意思是“同步”正是为了阻止其他线程在特定代码执行时访问另一个线程。所以这个代码最终会很精细。
最后要注意的是,如果存在'a'和'b'变量,而不仅仅是一个唯一变量'a'或其他任何名称,则无需同步此方法,因为它非常安全地访问其他变量(其他内存位置)。
class X {
private int a;
private int b;
public void addA(){
a++;
}
public void addB(){
b++;
}}
也会起作用
答案 9 :(得分:0)
这可能不起作用,因为从Integer到int和反向的装箱和自动装箱依赖于JVM,如果它们在-128到127之间,很可能会将两个不同的数字散列到相同的地址。
答案 10 :(得分:0)
在Java同步中,如果线程想要进入同步方法,它将获取该对象的所有同步方法的锁,而不仅仅是线程正在使用的一个同步方法。 因此,执行addA()的线程将获得对addA()和addB()的锁定,因为这两者都是同步的。因此具有相同对象的其他线程无法执行addB()。