1、synchronized深度解析

1.1、Synchronized关键字简介

 

 

2.1、Synchronized的地位

2.2、不使用并发手段的后果

 演示代码:

/**
 *
 * i++ 虽然是一行代码,但是实际上至少包含了一下三个动作
 * 1,读取 i的值
 * 2,计算 i 的值
 * 3,把 i+1 的计算结果写回到内存中
 */
public class ShowUnsafe1 implements Runnable {
    static ShowUnsafe1 r = new ShowUnsafe1();
    static int i = 0;

    @Override
    public void run() {

        for (int j = 0; j < 100000; j++) {
            i++;
        }

    }

    public static void main(String[] args) {
        Thread thread1 = new Thread(r);
        Thread thread2 = new Thread(r);
        thread1.start();
        thread2.start();
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main i =" + i);

    }
}

 两个线程进行10万次i++操作,最后结果小于20万。

3.1、Synchronized的两个用法

对象锁

        包括方法锁(默认锁对象为this当前实例对象)同步代码块锁(自己指定锁对象)

类锁

        指synchronized修饰静态的方法或指定锁为Class对象

第一个用法:对象锁

代码演示:对象锁的形式1-同步代码块

/**
 * synchronized 作用:能够保证在同一时刻最多只有一个线程执行该段代码,以保证并发安全的效果。
 * synchronized 可重入
 *
 */
public class SynchronizedObjectCodeBlock2  implements Runnable{
    private static SynchronizedObjectCodeBlock2 instance1 = new SynchronizedObjectCodeBlock2();
    private static SynchronizedObjectCodeBlock2 instance2 = new SynchronizedObjectCodeBlock2();
    Object lock1 = new Object();
    Object lock2 = new Object();
    @Override
    public void run() {
        synchronized (lock1){
            System.out.println("我是lock1对象锁的代码块的形式。我叫:"+Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"运行结束");
        }
        synchronized (lock2){
            System.out.println("我是lock2对象锁的代码块的形式。我叫:"+Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"运行结束");
        }


    }

    public static void main(String[] args) {
        Thread thread1 = new Thread(instance1);
        Thread thread2 = new Thread(instance1);
        thread1.start();
        thread2.start();
        while (thread1.isAlive() || thread2.isAlive() ){

        }
        System.out.println("main finished");
    }
}

结果:

我是lock1对象锁的代码块的形式。我叫:Thread-0
Thread-0运行结束
我是lock1对象锁的代码块的形式。我叫:Thread-1
我是lock2对象锁的代码块的形式。我叫:Thread-0
Thread-1运行结束
Thread-0运行结束
我是lock2对象锁的代码块的形式。我叫:Thread-1
Thread-1运行结束
main finished

代码演示:对象锁的形式2-普通方法锁

/**
 * 对象锁示例  方法锁
 */
public class SynchronizedObjectMethod3 implements Runnable{

    static SynchronizedObjectMethod3 instance = new SynchronizedObjectMethod3();
    @Override
    public void run() {

        method();
    }

    private synchronized void method() {
        System.out.println("我是 method 对象锁的代码块的形式。我叫:"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"运行结束");
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        System.out.println("main");
        while (t1.isAlive() || t2.isAlive()){

        }
        System.out.println("main finish");
    }
}

结果:

main
我是 method 对象锁的代码块的形式。我叫:Thread-0
Thread-0运行结束
我是 method 对象锁的代码块的形式。我叫:Thread-1
Thread-1运行结束
main finish

第二个用法:类锁

概念(重要):Java类可能有很多个对象,但只有1个Class对象。

形式1:synchronized加在static方法上

形式2:synchronized(*.class)代码块

代码演示:类锁的形式1-静态方法锁

/**
 * 类锁
 */
public class SynchronizedClassStatic4 implements Runnable{
    
    private static synchronized void method(){
        System.out.println("我是 method 类锁 我叫:"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"运行结束");
    }

    @Override
    public void run() {
        method();
    }
    public static void main(String[] args) {
        SynchronizedClassStatic4 instance1 = new SynchronizedClassStatic4();
        SynchronizedClassStatic4 instance2 = new SynchronizedClassStatic4();
        Thread thread1 = new Thread(instance1);
        Thread thread2 = new Thread(instance2);
        thread1.start();
        thread2.start();
        while (thread1.isAlive() || thread2.isAlive()){

        }
        System.out.println("main finish");

    }

}

结果:

我是 method 类锁 我叫:Thread-0
Thread-0运行结束
我是 method 类锁 我叫:Thread-1
Thread-1运行结束
main finish

代码演示:类锁的形式2-*.class

/**
 * 类锁
 */
public class SynchronizedClass5 implements Runnable{

    private void method(){
        synchronized (SynchronizedClass5.class){
            System.out.println("我是 *.class 类锁 我叫:"+Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"运行结束");
        }

    }

    @Override
    public void run() {
        method();
    }
    public static void main(String[] args) {
        SynchronizedClass5 instance1 = new SynchronizedClass5();
        SynchronizedClass5 instance2 = new SynchronizedClass5();
        Thread thread1 = new Thread(instance1);
        Thread thread2 = new Thread(instance2);
        thread1.start();
        thread2.start();
        while (thread1.isAlive() || thread2.isAlive()){

        }
        System.out.println("main finish");

    }

}

结果:

我是 *.class 类锁 我叫:Thread-0
Thread-0运行结束
我是 *.class 类锁 我叫:Thread-1
Thread-1运行结束
main finish

4.1、多线程访问同步方法的7种常见情况

1.两个线程同时访问一个对象的同步方法(可以实现同步效果)

/**
 * 对象锁示例  方法锁
 */
public class SynchronizedObjectMethod3 implements Runnable{

    static SynchronizedObjectMethod3 instance = new SynchronizedObjectMethod3();
    @Override
    public void run() {

        method();
    }

    private synchronized void method() {
        System.out.println("我是 method 对象锁的代码块的形式。我叫:"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"运行结束");
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        System.out.println("main");
        while (t1.isAlive() || t2.isAlive()){

        }
        System.out.println("main finish");
    }
}

结果:

main
我是 method 对象锁的代码块的形式。我叫:Thread-0
Thread-0运行结束
我是 method 对象锁的代码块的形式。我叫:Thread-1
Thread-1运行结束
main finish

2.两个线程同时访问两个对象的同步方法(起不到同步的效果)

/**
 * synchronized 作用:能够保证在同一时刻最多只有一个线程执行该段代码,以保证并发安全的效果。
 * synchronized 可重入
 *
 */
public class SynchronizedObjectCodeBlock2  implements Runnable{
    private static SynchronizedObjectCodeBlock2 instance1 = new SynchronizedObjectCodeBlock2();
    private static SynchronizedObjectCodeBlock2 instance2 = new SynchronizedObjectCodeBlock2();

    @Override
    public void run() {
        synchronized (this){
            System.out.println("我是lock1对象锁的代码块的形式。我叫:"+Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"运行结束");
        }

    }

    public static void main(String[] args) {
        Thread thread1 = new Thread(instance1);
        Thread thread2 = new Thread(instance2);
        thread1.start();
        thread2.start();
        while (thread1.isAlive() || thread2.isAlive() ){

        }
        System.out.println("main finished");
    }
}

结果:

我是lock1对象锁的代码块的形式。我叫:Thread-0
我是lock1对象锁的代码块的形式。我叫:Thread-1
Thread-1运行结束
Thread-0运行结束
main finished

3.两个线程访问的是synchronized的静态方法(可以实现同步效果)

/**
 * 类锁
 */
public class SynchronizedClassStatic4 implements Runnable{

    private static synchronized void method(){
        System.out.println("我是 method 类锁 我叫:"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"运行结束");
    }

    @Override
    public void run() {
        method();
    }
    public static void main(String[] args) {
        SynchronizedClassStatic4 instance1 = new SynchronizedClassStatic4();
        SynchronizedClassStatic4 instance2 = new SynchronizedClassStatic4();
        Thread thread1 = new Thread(instance1);
        Thread thread2 = new Thread(instance2);
        thread1.start();
        thread2.start();
        while (thread1.isAlive() || thread2.isAlive()){

        }
        System.out.println("main finish");

    }

}

结果:

我是 method 类锁 我叫:Thread-0
Thread-0运行结束
我是 method 类锁 我叫:Thread-1
Thread-1运行结束
main finish

4.同时访问同步方法与非同步方法(同步方法依然是最多只能被一个线程调用,非同步方法没有任何限制,谁来调用都可以,而且可以同时运行。)

/**
 * 同时访问同步方法和非同步方法
 */
public class SynchronizedYesAndNo6 implements Runnable {

    static SynchronizedYesAndNo6 instance = new SynchronizedYesAndNo6();

    private synchronized void method1() {
        System.out.println("我是 同步方法 的形式。我叫:"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"运行结束");
    }
    private  void method2() {
        System.out.println("我是 普通方法 的形式。我叫:"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"运行结束");
    }
    @Override
    public void run() {
        if(Thread.currentThread().getName().endsWith("Thread-0")){
            method1();
        }else {
            method2();
        }


    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        while (t1.isAlive() || t2.isAlive()){

        }
        System.out.println("main : finish");
    }
}

结果:

我是 同步方法 的形式。我叫:Thread-0
我是 普通方法 的形式。我叫:Thread-1
Thread-0运行结束
Thread-1运行结束
main : finish

5.访问同一个对象的不同的普通同步方法(可以实现同步)

/**
 * 访问同一个类的不同的普通同步方法
 */
public class SynchronizedDifferentMethod7 implements Runnable {
    private static SynchronizedDifferentMethod7 instance = new SynchronizedDifferentMethod7();

    private synchronized void method1() {
        System.out.println("我是 同步方法1的形式。我叫:"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"运行结束");
    }
    private  synchronized void method2() {
        System.out.println("我是 同步方法2 的形式。我叫:"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"运行结束");
    }
    @Override
    public void run() {
        if(Thread.currentThread().getName().endsWith("Thread-0")){
            method1();
        }else {
            method2();
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        while (t1.isAlive() || t2.isAlive()){

        }
        System.out.println("main finish");
    }
}

结果:

我是 同步方法1的形式。我叫:Thread-0
Thread-0运行结束
我是 同步方法2 的形式。我叫:Thread-1
Thread-1运行结束
main finish

6.同时访问静态synchronized和非静态synchronized方法(起不到同步作用,因为静态synchronized的.class类锁和非静态synchronized方法的对象锁是不同的锁。)

public class SynchronizedAndNormal8 implements Runnable{


    public  static  SynchronizedAndNormal8 instance = new SynchronizedAndNormal8();

    private static synchronized void method1() {
        System.out.println("我是 static 锁 的形式。我叫:"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"运行结束");
    }
    private  synchronized void method2() {
        System.out.println("我是 同步方法 的形式。我叫:"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"运行结束");
    }

    @Override
    public void run() {
        if(Thread.currentThread().getName().endsWith("Thread-0")){
            method1();
        }else {
            method2();
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        while (t1.isAlive() || t2.isAlive()){

        }
        System.out.println("main finish");
    }
}

结果:

我是 同步方法 的形式。我叫:Thread-1
我是 static 锁 的形式。我叫:Thread-0
Thread-0运行结束
Thread-1运行结束
main finish

7.方法抛出异常后,会释放锁。

public class SynchronizedException9 implements Runnable{


    public  static SynchronizedException9 instance = new SynchronizedException9();

    private  synchronized void method1() {
        System.out.println("我是 method1  的形式。我叫:"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName()+"运行结束");
        throw new RuntimeException("抛出异常");

    }
    private  synchronized void method2() {
        System.out.println("我是 method2 的形式。我叫:"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"运行结束");
    }

    @Override
    public void run() {
        if(Thread.currentThread().getName().endsWith("Thread-0")){
            method1();
        }else {
            method2();
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        while (t1.isAlive() || t2.isAlive()){

        }
        System.out.println("main finish");
    }
}

结果:

我是 method1  的形式。我叫:Thread-0
Thread-0运行结束
我是 method2 的形式。我叫:Thread-1
Exception in thread "Thread-0" java.lang.RuntimeException: 抛出异常
	at com.lvxiaosha.synchronized_practice.SynchronizedException9.method1(SynchronizedException9.java:17)
	at com.lvxiaosha.synchronized_practice.SynchronizedException9.run(SynchronizedException9.java:33)
	at java.base/java.lang.Thread.run(Thread.java:833)
Thread-1运行结束
main finish

Process finished with exit code 0

5.1、Synchronized的性质-可重入性质

  • 可重入
  • 不可中断

什么是可重入?

        指的是同一线程的外层函数获得锁之后,内层函数可以直接再次获取该锁。

好处

        避免死锁、提升封装性

可重入代码演示1:

/**
 * 证明 synchronized 可重入的
 */
public class SynchronizedRecursion10 implements Runnable{


    public  static SynchronizedRecursion10 instance = new SynchronizedRecursion10();
    int a = 0;
    private  synchronized void method1() {
        System.out.println("我是 method1  a = "+a);
        if(a == 0){
            a++;
            method1();
        }

    }
    private  synchronized void method2() {
        System.out.println("我是 method2 的形式。我叫:"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"运行结束");
    }

    @Override
    public void run() {
        if(Thread.currentThread().getName().endsWith("Thread-0")){
            method1();
        }else {
            method2();
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        while (t1.isAlive() || t2.isAlive()){

        }
        System.out.println("main finish");
    }
}

运行结果:

我是 method1  a = 0
我是 method1  a = 1
我是 method2 的形式。我叫:Thread-1
Thread-1运行结束
main finish

可重入代码演示2:

/**
 * 证明 synchronized 可重入的
 */
public class SynchronizedRecursion11 implements Runnable{


    public  static SynchronizedRecursion11 instance = new SynchronizedRecursion11();
    private  synchronized void method1() {
        System.out.println("我是 method1  : "+ Thread.currentThread().getName());
        method2();
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
    private  synchronized void method2() {
        System.out.println("我是 method2 我叫:"+Thread.currentThread().getName());


    }

    @Override
    public void run() {
        if(Thread.currentThread().getName().endsWith("Thread-0")){
            method1();
        }else {
            method2();
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        while (t1.isAlive() || t2.isAlive()){

        }
        System.out.println("main finish");
    }
}

运行结果:

我是 method1  : Thread-0
我是 method2 我叫:Thread-0
我是 method2 我叫:Thread-1
main finish

性质:不可中断

        一旦这个锁已经被别人获得了,如果我还想获得,只能选择等待或者阻塞,知道别的线程释放这个锁。如果别人永远不释放锁,那么我只能永远地等下去。

6.1、深入原理

加锁和释放锁的原理:看字节码

  • 获取和释放锁的时机:进入和退出同步代码块(包括抛出异常)
  • 等价代码
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 *  synchronized  能保证可见性
 *  用 lock 形式表现
 *  synchronized 效率低 锁的释放情况少,试图获得锁时不能设定超时,不能中断一个正在试图获得锁的线程。
 *  synchronized 不能中断。
 *  synchronized 不够灵活。
 *  无法知道是否成功了获取了锁。
 *  优先使用 synchronized 其次在使用lock
 */
public class SynchronizedToLock13 implements Runnable{

    Lock lock = new ReentrantLock();

    public  static SynchronizedToLock13 instance = new SynchronizedToLock13();
    private  synchronized void method1() {
        System.out.println("我是 synchronized  : "+ Thread.currentThread().getName());


    }
    private   void method2() {
        lock.lock();
        try{
            System.out.println("我是 lock 我叫:"+Thread.currentThread().getName());
        }finally {
            lock.unlock();
        }



    }

    @Override
    public void run() {
        if(Thread.currentThread().getName().endsWith("Thread-0")){
            method1();
        }else {
            method2();
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        while (t1.isAlive() || t2.isAlive()){

        }
        System.out.println("main finish");
    }
}

结果:

我是 synchronized  : Thread-0
我是 lock 我叫:Thread-1
main finish

看monitor指令

 

        可以看到有1个monitorenter,表示获取锁,还有2个monitorexit,表示释放锁,其中1个是正常释放锁,另外1个表示抛出异常后释放锁。

可重入原理:加锁次数计数器

  • JVM会记录被加锁的次数
  • 第一次加锁时,次数从0变成1,之后如果再次加锁,就从1变成2,以此类推。
  • 退出一层同步代码块时,计数减一,当计数为0的时候,代表锁释放。

保证可见性的原理:内存模型

  • 一个线程执行的结果,另外的线程不一定可见
  • 线程1操作x=5,之后线程2可能读取x=3
  • synchronized可以保证可见性

7.1、Synchronized的缺陷

  • 效率低:锁的释放情况少、试图获得锁时不能设定超时、不能中断一个正在试图获得锁的线程。
  • 不够灵活(读写锁更灵活):加锁和释放锁的时机单一,每个锁仅有单一的条件(某个对象),可能是不够的。
  • 无法知道是否成功的获取到锁

使用注意点:锁的范围不宜过大、避免锁的嵌套。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lvdapiaoliang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值