synchronized的特点,特点和基本使用以及优化机制

Synchronized 的特性:

  • synchronized可以保证原子性

    被 synchronized加锁的代码块只允许拿到锁的线程进入同步代码块/方法内部进行操作,也就是原子性的操作,以达到并发安全的效果.

  • synchronized可以保证有序性

    基于synchronized的原子性,导致线程的互斥, 就能够保证

    (这里注意,volatile保证的有序性和 synchronized保证的有序性是不同的,volatile 保证的有序性指的是禁止指令重排序而保证代码的有序性, synchronized保证的有序性指的是线程因为锁互斥的原因而保证之间的有序进行,因此就不会发生指令重排序这种情况, 因为这种情况发生在多线程同时修改的情况下.volatile详情点击这篇博客)

  • synchronized的特点:

    • 既是乐观锁, 也是悲观锁

    • 既是轻量级锁, 也是重量级锁

    • 轻量级锁大概率基于自旋实现, 重量级锁基于挂起等待实现,

    • 不是读写锁,

    • 是可重入锁

    • 是非公平锁

Synchronized 的使用

  • synchronized可以修饰代码块

    synchronized修饰代码块的时候相当于这块代码块就是一个同步的,保证这个代码块是一个原子性的,只允许一个线程在代码块内部操作.

    可以创建多种锁对象来对代码块进行加锁

    • 对实例本身作为锁来进行加锁操作.
    public class Blog_synchronized {
        public static void main(String[] args) {
            Blog_synchronized blog = new Blog_synchronized();
            Thread t1 = new Thread(() -> {
                blog.test1();
            },"t1");
            Thread t2 = new Thread(() -> {
                blog.test1();
            },"t2");
            t1.start();
            t2.start();
        }
        public void test1() {
            //1. 修饰代码块
            // 保证只有t2 调用该语句
            if (Thread.currentThread().getName().equals("t2")) {
                System.out.println("t2在外部等待t1内部休眠!");
            }
            synchronized (this) {
                System.out.println(Thread.currentThread().getName() + " 调用了 test1! " );
                // 休眠10秒
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    
    

请添加图片描述请添加图片描述

  • 创建一个专用的锁对象,拿到锁对象的线程才可以进入同步代码块内

    public class Blog_synchronized {
        // 锁对象
        static final Object locker = new Object();
        public static void main(String[] args) {
            Blog_synchronized blog = new Blog_synchronized();
            Thread t1 = new Thread(() -> {
                blog.test1();
            },"t1");
            Thread t2 = new Thread(() -> {
                blog.test1();
            },"t2");
            t1.start();
            t2.start();
        }
        public void test1() {
            //1. 修饰代码块
            // 保证只有t2 调用该语句
            if (Thread.currentThread().getName().equals("t2")) {
                System.out.println("t2在外部等待t1内部休眠!");
            }
            synchronized (locker) {
                System.out.println(Thread.currentThread().getName() + " 调用了 test1! " );
                // 休眠10秒
                try {
                    System.out.println(Thread.currentThread().getName() +"休眠10秒" + "之后再释放locker");
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

请添加图片描述
请添加图片描述

  • 对类对象加锁

因为一个类只能由一个类对象(唯一), 也就是可以通过getClass()方法查看获取的类对象( 类.class ), 因此我们可以将类对象作为锁对象来进行加锁,

public class Blog_synchronized {
    public static void main(String[] args) throws InterruptedException {
        Blog_synchronized blog = new Blog_synchronized();
        Thread t1 = new Thread(() -> {
            blog.test3();
        },"t1");
        Thread t2 = new Thread(() -> {
            blog.test3();
        },"t2");
        t1.start();
        t2.start();
        Thread.sleep(1000);
        System.out.println("t2 状态: " + t2.getState());
    }
    public void test3(){
        if (Thread.currentThread().getName().equals("t2")) {
            System.out.println("t2在外部等待t1内部休眠!");
        }
        synchronized (Blog_synchronized.class) {
            try {
                System.out.println(Thread.currentThread().getName() + "调用了test3!进入同步代码块内部!");
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


请添加图片描述
请添加图片描述

t2进入后…

请添加图片描述

  • synchronized 可以修饰方法

​ 当synchronized修饰方法的时候其实等价于 synchronized(this) 因为只有能抢到实例对象的线程才可以调用该方法

public class Blog_synchronized {
    public static void main(String[] args) throws InterruptedException {
        Blog_synchronized blog = new Blog_synchronized();
        Thread t1 = new Thread(() -> {
            blog.test2();
        },"t1");
        Thread t2 = new Thread(() -> {
            blog.test2();
        },"t2");
        t1.start();
        t2.start();
        Thread.sleep(1000);
        System.out.println(t2.getState());
    }
    synchronized public void test2() {
        System.out.println(Thread.currentThread().getName() + "调用了test2!");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

当t1 占用该方法的时候 , 此时t2就是再不断自旋尝试获取加锁方法, 因此就是一个Blocked阻塞态

请添加图片描述
请添加图片描述

t1 释放了锁之后…
请添加图片描述

Synchronized的优化锁机制

锁膨胀/ 锁升级

体现了synchronized 能够"自适应" 能力

请添加图片描述

锁粗化

锁的粗细就指的是"锁的粒度"(加锁代码涉及范围,加锁代码的范围越大,锁的粒度越粗 ,加锁代码范围越小,锁的粒度越细)

也就是锁粒度越细 优点: 说明多个线程的并发性就更高,

如果锁粒度比较粗 优点: 就说明加锁解锁的开销就更小,

因此编译器就会有一个优化 自动判定 如果某个代码锁的粒度太细, 就会进行粗化 ( 扩大加锁代码的范围 )

如果两次加锁之间的间隔较大,(中间隔的代码多) ,一般不会优化,

如果两次加锁之间的间隔较小(中间隔的代码少) , 就很有可能出发这个优化.

锁消除

有些代码,明明不用加锁 , 但是你还是加锁了,编译器就会发现加锁好像没有必要, 此时就直接把锁给去掉了.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值