Answers:
如果将方法声明为已同步(就像通过键入所做的那样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();
}
}
a
和b
是对象(例如)Integer
,则在应用运算符时,您正在用不同的对象替换实例++
。
为此,在方法声明上同步的是语法糖:
public void addA() {
synchronized (this) {
a++;
}
}
在静态方法上,它是语法糖:
ClassA {
public static void addA() {
synchronized(ClassA.class) {
a++;
}
}
我认为,如果Java设计人员知道现在对同步有什么了解,他们就不会添加语法糖,因为它经常会导致并发的不良实现。
从“ Java™教程”中的同步方法开始:
首先,不可能对同一对象的两次同步方法调用进行交织。当一个线程正在执行对象的同步方法时,所有其他线程调用同一对象块的同步方法(挂起执行),直到第一个线程对该对象完成。
在同步块上的“ Java™教程”中:
同步语句对于通过细粒度同步提高并发性也很有用。例如,假设类MsLunch有两个实例字段c1和c2,它们从未一起使用。这些字段的所有更新都必须同步,但是没有理由阻止c1更新与c2更新交织 -这样做会通过创建不必要的阻塞来减少并发性。代替使用同步方法或以其他方式使用与此关联的锁,我们仅创建两个对象来提供锁。
(强调我的)
假设您有2个非交织变量。因此,您希望同时从不同的线程访问每个线程。您需要定义锁不上对象类本身,而是对类对象像下面(例如,从第二个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++;
}
}
}
访问的锁在对象上,而不在方法上。在该方法中访问哪些变量无关紧要。
在该方法中添加“已同步”意味着运行代码的线程必须在继续操作之前获取该对象的锁。添加“静态同步”意味着运行代码的线程必须在继续之前获得对类对象的锁定。或者,您可以将代码包装在这样的块中:
public void addA() {
synchronized(this) {
a++;
}
}
这样您就可以指定必须获取其锁的对象。
如果要避免锁定包含对象,则可以选择:
如果您有一些方法不同步,并且正在访问和更改实例变量。在您的示例中:
private int a;
private int b;
当其他线程处于同一对象的同步方法中时,任意数量的线程可以同时访问这些非同步方法,并且可以更改实例变量。例如:
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。
是的,它将阻止另一个方法,因为同步方法将按指向的方式应用于WHOLE类对象。但是无论如何,它将在执行所输入的任何方法addA或addB的总和时仅阻止其他线程执行,因为完成时...一个线程将释放对象,而另一个线程将访问另一种方法,以此类推。
我的意思是“同步”是专门为阻止另一个线程在特定代码执行中访问另一个线程而制作的。最终,此代码将很有效。
最后要注意的是,如果有一个'a'和'b'变量,而不仅仅是一个唯一变量'a'或其他名称,则无需同步此方法,因为它可以安全地访问其他var(其他内存)位置)。
class X {
private int a;
private int b;
public void addA(){
a++;
}
public void addB(){
b++;
}}
也会工作
这个示例(尽管不是一个很好的示例)可以提供有关锁定机制的更多信息。如果增量型是同步的,并incrementB是不同步的,那么incrementB会尽快执行,但如果incrementB也同步那么它必须“等待”的增量型到结束,前incrementB可以完成自己的工作。
这两个方法都被调用到单个实例对象上,在本例中为:job和“竞争”线程分别是aThread和main。
如果在没有增量的情况下尝试在增量B中使用“已同步 ”,则会看到不同的结果。如果增量B也已“ 同步 ”,则必须等待增量A()完成。每个变体运行几次。
class LockTest implements Runnable {
int a = 0;
int b = 0;
public synchronized void incrementA() {
for (int i = 0; i < 100; i++) {
this.a++;
System.out.println("Thread: " + Thread.currentThread().getName() + "; a: " + this.a);
}
}
// Try with 'synchronized' and without it and you will see different results
// if incrementB is 'synchronized' as well then it has to wait for incrementA() to finish
// public void incrementB() {
public synchronized void incrementB() {
this.b++;
System.out.println("*************** incrementB ********************");
System.out.println("Thread: " + Thread.currentThread().getName() + "; b: " + this.b);
System.out.println("*************** incrementB ********************");
}
@Override
public void run() {
incrementA();
System.out.println("************ incrementA completed *************");
}
}
class LockTestMain {
public static void main(String[] args) throws InterruptedException {
LockTest job = new LockTest();
Thread aThread = new Thread(job);
aThread.setName("aThread");
aThread.start();
Thread.sleep(1);
System.out.println("*************** 'main' calling metod: incrementB **********************");
job.incrementB();
}
}
在Java同步中,如果一个线程想要进入同步方法,它将获取该对象的所有同步方法的锁,而不仅仅是该线程正在使用的一个同步方法。因此执行addA()的线程将获得对addA()和addB()的锁定,因为这两个线程都是同步的。因此具有相同对象的其他线程无法执行addB()。
这可能不起作用,因为从Integer到int的装箱和自动装箱取决于JVM,反之亦然,这取决于JVM,如果两个不同的数字介于-128和127之间,则很可能会将两个不同的数字散列到同一地址。
synchronized (this)
主体周围具有一个块。对象“ this”不会被锁定,而是将对象“ this”用作互斥对象,并防止主体与也与此“ this”同步的其他代码段同时执行。它对未同步的“ this”的其他字段/方法没有影响。