wun*_*tee 176 java multithreading locking synchronized thread-safety
如果我在同一个类中有2个同步方法,但每个方法访问不同的变量,那么2个线程可以同时访问这两个方法吗?锁是否发生在对象上,或者它是否与synchronized方法中的变量一样具体?
例:
class X {
private int a;
private int b;
public synchronized void addA(){
a++;
}
public synchronized void addB(){
b++;
}
}
Run Code Online (Sandbox Code Playgroud)
2个线程可以同时访问同一个X类实例x.addA()x.addB()吗?
Osc*_*Ryz 181
如果您将方法声明为同步(正如您通过键入操作public synchronized void addA()),则在整个对象上进行同步,因此从同一对象访问不同变量的两个线程无论如何都会相互阻塞.
如果你希望只对一个变量在同一时间同步,所以在访问不同的变量两个线程不会阻塞对方,你对他们分别在同步synchronized ()块.如果a和b是对象引用,你可以使用:
public void addA() {
synchronized( a ) {
a++;
}
}
public void addB() {
synchronized( b ) {
b++;
}
}
Run Code Online (Sandbox Code Playgroud)
但由于他们是原始人,你不能这样做.
我建议你改用AtomicInteger:
import java.util.concurrent.atomic.AtomicInteger;
class X {
AtomicInteger a;
AtomicInteger b;
public void addA(){
a.incrementAndGet();
}
public void addB(){
b.incrementAndGet();
}
}
Run Code Online (Sandbox Code Playgroud)
Yis*_*hai 66
在方法声明上同步是这样的语法糖:
public void addA() {
synchronized (this) {
a++;
}
}
Run Code Online (Sandbox Code Playgroud)
在静态方法上,它是语法糖:
ClassA {
public static void addA() {
synchronized(ClassA.class) {
a++;
}
}
Run Code Online (Sandbox Code Playgroud)
我认为如果Java设计者知道现在对同步的理解,他们就不会添加语法糖,因为它往往会导致并发性的不良实现.
小智 18
首先,对同一对象的两个同步方法的调用不可能进行交错.当一个线程正在为对象执行同步方法时,所有其他线程调用同一对象的同步方法(暂停执行)直到第一个线程完成对象.
同步语句对于通过细粒度同步提高并发性也很有用.例如,假设类MsLunch有两个从不一起使用的实例字段c1和c2.必须同步这些字段的所有更新,但没有理由阻止c1的更新与c2的更新交错 - 这样做会通过创建不必要的阻塞来减少并发性.我们创建两个对象仅用于提供锁,而不是使用同步方法或以其他方式使用与此关联的锁.
(强调我的.)
你有2个变量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++;
}
}
}
Run Code Online (Sandbox Code Playgroud)
Nat*_*hes 14
访问的锁是在对象上,而不是在方法上.在该方法中访问哪些变量是无关紧要的.
将"synchronized"添加到方法意味着运行代码的线程必须在继续之前获取对象的锁定.添加"静态同步"意味着运行代码的线程必须在继续之前获取类对象的锁定.或者,您可以将代码包装在一个块中,如下所示:
public void addA() {
synchronized(this) {
a++;
}
}
Run Code Online (Sandbox Code Playgroud)
这样您就可以指定必须获取其锁定的对象.
如果要避免锁定包含对象,可以选择:
如果您有一些未同步的方法并且正在访问和更改实例变量。在你的例子中:
private int a;
private int b;
Run Code Online (Sandbox Code Playgroud)
当其他线程处于同一对象的同步方法中并且可以更改实例变量时,任意数量的线程都可以同时访问这些非同步方法。例如:-
public void changeState() {
a++;
b++;
}
Run Code Online (Sandbox Code Playgroud)
您需要避免非同步方法访问实例变量并更改它的情况,否则没有必要使用同步方法。
在以下情况下:-
class X {
private int a;
private int b;
public synchronized void addA(){
a++;
}
public synchronized void addB(){
b++;
}
public void changeState() {
a++;
b++;
}
}
Run Code Online (Sandbox Code Playgroud)
只有一个线程可以进入addA或addB方法,但同时任意数量的线程可以进入changeState方法。没有两个线程可以同时进入 addA 和 addB (因为对象级锁定),但同时任意数量的线程可以进入changeState。