1、简介
synchronized是Java中的关键字。
可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性
2、synchronized有三种应用方式:
修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
示例1 不加 synchronized 处理
//共享变量
private static int inc = 0;
private static final SynchronizedDemo demo = new SynchronizedDemo();
private static void demo01() {
Thread t1 = new Thread(demo::method01, "t1");
Thread t2 = new Thread(demo::method01, "t2");
Thread t3 = new Thread(demo::method01, "t3");
t1.start();
t2.start();
t3.start();
// 线程礼让,子线程跑完,主线程才跑
while (Thread.activeCount() > 1) Thread.yield();
System.out.println("inc =" + inc);
}
private void method01() {
common();
}
private static void common() {
String thName = Thread.currentThread().getName();
System.out.println("线程:" + thName + " is ready >> " + inc);
try {
Thread.sleep(1000);
for (int i = 0; i < 1000; i++) {
inc++;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + thName + " is over >> " + inc);
}
多次测试,不能保证最终结果一致
示例2 synchronized 修饰实例方法
private static void demo02() {
Thread t1 = new Thread(demo::method02, "t1");
Thread t2 = new Thread(demo::method02, "t2");
Thread t3 = new Thread(demo::method02, "t3");
t1.start();
t2.start();
t3.start();
while (Thread.activeCount() > 1) Thread.yield();
System.out.println("inc =" + inc);
}
private synchronized void method02() {
common();
}
当多线程同时对一个对象的一个实例方法进行操作时,只有一个线程能够抢到锁。
因为一个对象只有一把锁,一个线程获取了该对象的锁之后,其他线程就无法获取该对象的锁了。
多次测试,能保证最终结果一致
示例3 synchronized 与 非synchronized 交叉调用
private static void demo02_2() {
// method01 非synchronized
Thread t1 = new Thread(demo::method01, "t1");
// method02 synchronized
Thread t2 = new Thread(demo::method02, "t2");
t1.start();
t2.start();
while (Thread.activeCount() > 1) Thread.yield();
System.out.println("inc =" + inc);
}
一个线程获取了该对象的锁之后,其他线程可以访问其他非synchronized实例方法,但无法访问其他synchronized实例方法
多次测试,不能保证最终结果一致
示例4 synchronized 作用于不同的实例对象
private static void demo02_3() {
SynchronizedDemo demo2 = new SynchronizedDemo();
Thread t1 = new Thread(demo::method02, "t1");
Thread t2 = new Thread(demo2::method02, "t2");
t1.start();
t2.start();
while (Thread.activeCount() > 1) Thread.yield();
System.out.println("inc =" + inc);
}
因为两个线程作用于不同的对象,获得的是不同的锁,所以互相并不影响
多次测试,不能保证最终结果一致
示例5 synchronized 修饰静态方法
private static void demo03() {
Thread t1 = new Thread(SynchronizedDemo::method03, "t1");
Thread t2 = new Thread(SynchronizedDemo::method03, "t2");
Thread t3 = new Thread(SynchronizedDemo::method03, "t3");
t1.start();
t2.start();
t3.start();
while (Thread.activeCount() > 1) Thread.yield();
System.out.println("inc =" + inc);
}
private static synchronized void method03() {
common();
}
作用于静态方法,此时的锁是类对象,所以实例对象共享同一把锁
多次测试,能保证最终结果一致
示例6 synchronized 作用于代码块
private static void demo04() {
Thread t1 = new Thread(demo::method04, "t1");
Thread t2 = new Thread(demo::method04, "t2");
Thread t3 = new Thread(demo::method04, "t3");
t1.start();
t2.start();
t3.start();
while (Thread.activeCount() > 1) Thread.yield();
System.out.println("inc =" + inc);
}
private void method04() {
String thName = Thread.currentThread().getName();
System.out.println("线程:" + thName + " is ready >> " + inc);
try {
Thread.sleep(1000);
synchronized (this) {
System.out.println("线程:" + thName + " 持有锁");
for (int i = 0; i < 1000; i++) {
inc++;
}
System.out.println("线程:" + thName + " 释放锁");
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("线程:" + thName + " is over >> " + inc);
}
多次测试,能保证最终结果一致
示例7 可重入性
private static void demo05() {
Thread t1 = new Thread(demo::method05, "t1");
Thread t2 = new Thread(demo::method05, "t2");
Thread t3 = new Thread(demo::method05, "t3");
t1.start();
t2.start();
t3.start();
}
private synchronized void method05() {
System.out.println("线程:" + Thread.currentThread().getName() + " 执行了method05");
method05_2();
}
private synchronized void method05_2() {
System.out.println("线程:" + Thread.currentThread().getName() + " 执行了method05_2");
}
获取锁后进入synchronized同步代码,并在代码块中调用了当前实例对象的另外一个synchronized方法, 再次申请锁时,将被允许。这就是重入锁最直接的体现
示例8 继承之可重入
private static void demo06() {
SyncDemo sync = new SyncDemo();
Thread t1 = new Thread(sync::method06, "t1");
Thread t2 = new Thread(sync::method06, "t2");
Thread t3 = new Thread(sync::method06, "t3");
t1.start();
t2.start();
t3.start();
}
public synchronized void method06() {
System.out.println("线程:" + Thread.currentThread().getName() + " 执行了父方法");
}
static class SyncDemo extends SynchronizedDemo {
public synchronized void method06() {
System.out.println("线程:" + Thread.currentThread().getName() + " 执行了子方法");
super.method06();
}
}
当子类继承父类时,子类也是可以通过可重入锁调用父类的同步方法。
注意由于synchronized是基于monitor实现的,因此每次重入,monitor中的计数器仍会加1。
3、Synchronized的特点
1.Java中的关键字
2.原子性和可见性
3.自动获取、释放锁
4.可应用于代码块、实例方法、静态方法
5.可重入