synchronized关键字是java中用于解决多线程并发问题的一种机制,它可以确保同一时刻只有一个线程能够访问共享资源,从而避免了数据竞争和其它并发问题。
注意:为了保证逻辑中变量count�的原子性,其类型使用AtomicInteger�。
public class test {
private static AtomicInteger count = new AtomicInteger();
public static void main(String[] args) {
Counter counter = new Counter();
Thread thread1 = new Thread(() -> { //线程1
counter.increase(count);
});
Thread thread2 = new Thread(() -> { //线程2
counter.increase(count);
});
thread1.start();
thread2.start();
}
}
class Counter {
public synchronized void increase(AtomicInteger count) {
count.incrementAndGet();
System.out.println(Thread.currentThread().getName() + ":" + count);
}
}
输出结果为:
说明:因为thread0中的increase()方法和thread1中的increase()方法都是通过counter对象调用,它们用的是同一个对象锁,互斥。所以,thread1一直等到thread0中的increase()方法执行完释放后才开始执行thread1的increase()方法。
修改代码,每个线程分别创建一个Counter对象:
public class test {
private static AtomicInteger count = new AtomicInteger();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> { //线程1
Counter counter1 = new Counter();
counter1.increase(count);
});
Thread thread2 = new Thread(() -> { //线程2
Counter counter2 = new Counter();
counter2.increase(count);
});
thread1.start();
thread2.start();
}
}
class Counter {
public synchronized void increase(AtomicInteger count) {
count.incrementAndGet();
System.out.println(Thread.currentThread().getName() + ":" + count);
}
}
输出结果为:
说明:因为thread0中的increase()方法是通过counter1对象调用的,thread1中的increase()方法是通过counter2对象调用,它们用的是两个不同的对象锁,并不互斥。所以,thread1中的increase()方法和thread0中的increase()方法是同时进行的。
结论:使用"同一对象"去调用其被synchronized修饰的普通方法才互斥。
public class test {
private static AtomicInteger count = new AtomicInteger();
public static void main(String[] args) {
Counter counter = new Counter();
Thread thread1 = new Thread(() -> { //线程1
counter.increase(count);
});
Thread thread2 = new Thread(() -> { //线程2
counter.increase(count);
});
thread1.start();
thread2.start();
}
}
class Counter {
public synchronized static void increase(AtomicInteger count) {
count.incrementAndGet();
System.out.println(Thread.currentThread().getName() + ":" + count);
}
}
输出结果为:
修改代码,每个线程分别创建一个Counter对象:
public class test {
private static AtomicInteger count = new AtomicInteger();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> { //线程1
Counter counter1 = new Counter();
counter1.increase(count);
});
Thread thread2 = new Thread(() -> { //线程2
Counter counter2 = new Counter();
counter2.increase(count);
});
thread1.start();
thread2.start();
}
}
class Counter {
public synchronized static void increase(AtomicInteger count) {
count.incrementAndGet();
System.out.println(Thread.currentThread().getName() + ":" + count);
}
}
输出结果为:
说明:因为increase()方法是Counter类里的静态方法,并且被synchronized修饰,所以这个方法是靠唯一的Counter类锁工作,所以无论是通过Counter类调用还是new不同的counter对象,都是互斥的。
结论: 被synchronized修饰的静态方法靠类锁工作。当多个线程同时访问某个被synchronized修饰的静态方法时,一旦某个进程抢得该类的类锁后,其它进程只能排队。如果该类包含多个被synchronized修饰的静态方法,只要有一个静态方法被某一线程获得类锁,即使其它线程调用其它被synchronized修饰的静态方法也需要排队。
它的原理是在方法的flags中增加ACC_SYNCHRONIZED标记,有ACC_SYNCHRONIZED标记的方法在被调用时,调用指令会先去检查方法的ACC_SYNCHRONIZED访问标志是否设置,如果设置了执行线程先要持有同步锁,然后才去执行方法,否则相关线程会被阻塞。
通过以上第一个修饰普通方法代码,反编译increase()结果如下(javap -c -v Counter.class):
注意:方法要public修饰,否则在反编译代码中看不到。
从反编译结果可以看出ACC_SYNCHRONIZED标记,表明该方法是synchronized的,同时还可能会有ACC_PUBLIC和ACC_STATIC,这两个标记分别表示访问权限和是否是静态方法。
public class test {
private static AtomicInteger count = new AtomicInteger();
public static void main(String[] args) {
Counter counter = new Counter();
Thread thread1 = new Thread(() -> { //线程1
counter.increase(count);
});
Thread thread2 = new Thread(() -> { //线程2
counter.increase(count);
});
thread1.start();
thread2.start();
}
}
class Counter {
public void increase(AtomicInteger count) {
synchronized (Counter.class) {
count.incrementAndGet();
System.out.println(Thread.currentThread().getName() + ":" + count);
}
}
}
输出结果为:
修改代码,每个线程分别创建一个Counter对象:
public class test {
private static AtomicInteger count = new AtomicInteger();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> { //线程1
Counter counter1 = new Counter();
counter1.increase(count);
});
Thread thread2 = new Thread(() -> { //线程2
Counter counter2 = new Counter();
counter2.increase(count);
});
thread1.start();
thread2.start();
}
}
class Counter {
public void increase(AtomicInteger count) {
synchronized (Counter.class) {
count.incrementAndGet();
System.out.println(Thread.currentThread().getName() + ":" + count);
}
}
}
输出结果:
结论:synchronized在修饰class时,同步代码块只有同一个类的实例访问。因此,如果有多个实例属于同一个类,它们将共享同一个锁,并且只能有一个线程同时执行该类的同步代码块。所以,即使创建多个对象也会排序执行被锁的代码块。
public class test {
private static AtomicInteger count = new AtomicInteger();
public static void main(String[] args) {
Counter counter = new Counter();
Thread thread1 = new Thread(() -> { //线程1
counter.increase(count);
});
Thread thread2 = new Thread(() -> { //线程2
counter.increase(count);
});
thread1.start();
thread2.start();
}
}
class Counter {
public void increase(AtomicInteger count) {
synchronized (this) {
count.incrementAndGet();
System.out.println(Thread.currentThread().getName() + ":" + count);
}
}
}
输出结果:
修改代码,每个线程分别创建一个Counter对象:
public class test {
private static AtomicInteger count = new AtomicInteger();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> { //线程1
Counter counter1 = new Counter();
counter1.increase(count);
});
Thread thread2 = new Thread(() -> { //线程2
Counter counter2 = new Counter();
counter2.increase(count);
});
thread1.start();
thread2.start();
}
}
class Counter {
public void increase(AtomicInteger count) {
synchronized (this) {
count.incrementAndGet();
System.out.println(Thread.currentThread().getName() + ":" + count);
}
}
}
输出结果:
结论:synchronized在修饰this时,只能确保同一对象下所有线程代码之间的同步代码块互斥(线程安全),而不同对象的的线程是不受影响的(线程不安全)。
public class test {
private static AtomicInteger count = new AtomicInteger();
public static void main(String[] args) {
Counter counter = new Counter();
Thread thread1 = new Thread(() -> { //线程1
counter.increase(count);
});
Thread thread2 = new Thread(() -> { //线程2
counter.increase(count);
});
thread1.start();
thread2.start();
}
}
class Counter {
private Object lockObj = new Object();
public void increase(AtomicInteger count) {
synchronized (lockObj) {
count.incrementAndGet();
System.out.println(Thread.currentThread().getName() + ":" + count);
}
}
}
输出结果:
修改代码,每个线程分别创建一个Counter对象:
public class test {
private static AtomicInteger count = new AtomicInteger();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> { //线程1
Counter counter1 = new Counter();
counter1.increase(count);
});
Thread thread2 = new Thread(() -> { //线程2
Counter counter2 = new Counter();
counter2.increase(count);
});
thread1.start();
thread2.start();
}
}
class Counter {
private Object lockObj = new Object();
public void increase(AtomicInteger count) {
synchronized (lockObj) {
count.incrementAndGet();
System.out.println(Thread.currentThread().getName() + ":" + count);
}
}
}
输出结果:
结论:synchronized在修饰非静态object时,只能确保同一对象下所有线程代码之间的同步代码块互斥(线程安全),而不同对象的的线程是不受影响的(线程不安全),类似于this。
public class test {
private static AtomicInteger count = new AtomicInteger();
public static void main(String[] args) {
Counter counter = new Counter();
Thread thread1 = new Thread(() -> { //线程1
counter.increase(count);
});
Thread thread2 = new Thread(() -> { //线程2
counter.increase(count);
});
thread1.start();
thread2.start();
}
}
class Counter {
private static Object lockObj = new Object();
public void increase(AtomicInteger count) {
synchronized (lockObj) {
count.incrementAndGet();
System.out.println(Thread.currentThread().getName() + ":" + count);
}
}
}
输出结果:
修改代码,每个线程分别创建一个Counter对象:
public class test {
private static AtomicInteger count = new AtomicInteger();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> { //线程1
Counter counter1 = new Counter();
counter1.increase(count);
});
Thread thread2 = new Thread(() -> { //线程2
Counter counter2 = new Counter();
counter2.increase(count);
});
thread1.start();
thread2.start();
}
}
class Counter {
private static Object lockObj = new Object();
public void increase(AtomicInteger count) {
synchronized (lockObj) {
count.incrementAndGet();
System.out.println(Thread.currentThread().getName() + ":" + count);
}
}
}
输出结果:
结论:synchronized在修饰静态object时,同步代码块只有同一个类的实例访问。因此,如果有多个实例属于同一个类,它们将共享同一个锁,并且只能有一个线程同时执行该类的同步代码块。所以,即使创建多个对象也会排序执行被锁的代码块,类似于class。
synchronized修饰代码块是通过monitorenter和monitorexit指令进行同步处理的。monitorenter指令用于进入synchronized代码块或方法,它会尝试获取一个锁,如果锁已经被其它线程持有,则该线程将被阻塞;monitorexit指令用于退出synchronized代码块或方法,它会释放锁,这样其它等待的线程就可以获取到锁并执行。
通过以上第一个修饰class代码,反编译increase()结果如下(javap -c -v Counter.class):