博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
谈一谈synchronized关键词
阅读量:5317 次
发布时间:2019-06-14

本文共 2436 字,大约阅读时间需要 8 分钟。

1.使用

java中的每一个对象都可以作为synchronized的锁进行代码同步,常见的形式

  • 同步代码块锁是synchronized括号内的对象
  • 普通成员方法上,锁是当前的对象,synchronized(this)
  • 静态方法上,锁是当前类的Class对象

2. 原理

synchronized是通过指定某个对象进行加锁,那么synchronized的锁信息肯定是和对象有关。Java的对象头里的Mark Word字段,默认是存储对象的HashCode。32位虚拟机中Mark Word的存储结构

锁状态 Mark Word中的存储内容 标志位
无锁状态 Hash code,偏向锁0 01
偏向锁 线程ID,偏向锁1 01
轻量级锁 指向栈中锁记录的指针 00
重量级锁 指向互斥量的指针 10

3.锁升级

偏向锁 ==> 轻量级锁 ==> 重量级锁

锁只能升级、不能退化

1.偏向锁

  • 偏向锁加锁:Mark Word中记录了当前获取锁线程ID,这样同一个线程可以多次进入同一个共享代码块而无需加锁。如果是无锁状态,则尝试通过CAS将偏向锁中的线程id修改为当前线程。
  • 偏向锁解锁:需要等到全局安全点(在这个时间点上没有正在执行的字节码)会首先暂停拥有偏向锁的线程,判断锁对象是否处于被活动状态,撤销偏向锁后恢复到未锁定或升级为轻量级锁。

image

2.轻量级锁

  • 加锁:在栈帧中创建空间(Displaced Mark Word)存储Mark Word中内容,然后尝试使用Cas把Mark word中的指针指向栈帧。成功则获取锁,失败则通过自旋获取锁,自旋失败则升级为重量级锁
  • 解锁:通过CAS把栈帧中的(Displaced Mark Word使用CAS替换回Mark Word,成功则说明在期间没有锁竞争,否则唤醒等待线程(已经升级为重量级锁)
    image

3.锁的优缺点

优点 缺点 适用场景
偏向锁 加锁和解锁不需要额外的消耗,和执行非同步方法比仅存在纳秒级的差距 如果线程间存在锁竞争,会带来额外的锁撤销的消耗 适用于只有一个线程访问同步块场景
轻量级锁 竞争的线程不会阻塞,提高了程序的响应速度 如果始终得不到锁竞争的线程使用自旋会消耗CPU 追求响应时间,锁占用时间很短
重量级锁 线程竞争不使用自旋,不会消耗CPU 线程阻塞,响应时间缓慢 追求吞吐量,锁占用时间较长

4. StringBuilder和StringBuffer

如果你看到别人在非并发环境下使用StringBuffer就说,你这里应该用StringBuilder啊,用StringBuffer明显影响性能。这句话前面半句是没问题的,但是说明显影响性能这个就有点显得不那么专业了。如果你看明白的了synchronized的锁升级就应该知道,在单线程环境下永远是偏向锁,不会升级。因此性能开销可以忽略不计。

As of  release JDK 5, this class has been supplemented with an equivalent class designed for use by a single thread, link StringBuilder.  The StringBuilder class should generally be used in preference to this one, as it supports all of the same operations but it is faster, as t performs no synchronization.

5. synchronized + String

public class TTT {    public static void main(String[] args) throws ParseException, InterruptedException {        new MyThread("A").start();        synchronized (new String("B")) {            new MyThread("B").start();        }        new MyThread("A").start();    }    public static void synchronizeMethod(String str) throws InterruptedException {        synchronized (str) {            TimeUnit.SECONDS.sleep(2);            System.out.println(str);        }    }    static class MyThread extends Thread {        String str;                public MyThread(String str) {            this.str = str;        }        @Override        public void run() {            try {                synchronizeMethod(str);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

 你知道上面代码的执行情况吗。如果不明白那说明synchronized使用和String的内存分配情况还有欠缺。

转载于:https://www.cnblogs.com/lizo/p/7570740.html

你可能感兴趣的文章
Scala入门(1)Linux下Scala(2.12.1)安装
查看>>
如何改善下面的代码 领导说了很耗资源
查看>>
php中的isset和empty的用法区别
查看>>
Android ViewPager 动画效果
查看>>
博弈论
查看>>
Redis sentinel & cluster 原理分析
查看>>
我的工作习惯小结
查看>>
把word文档中的所有图片导出
查看>>
浏览器的判断;
查看>>
ubuntu 18.04取消自动锁屏以及设置键盘快捷锁屏
查看>>
Leetcode 589. N-ary Tree Preorder Traversal
查看>>
机器学习/深度学习/其他开发环境搭建记录
查看>>
xml.exist() 实例演示
查看>>
判断是否为空然后赋值
查看>>
zabbix监控日志文件
查看>>
正则表达式
查看>>
pip install torch on windows, and the 'from torch._C import * ImportError: DLL load failed:' s...
查看>>
java基础(一):我对java的三个环境变量的简单理解和配置
查看>>
arcgis api 4.x for js 结合 Echarts4 实现散点图效果(附源码下载)
查看>>
YTU 2625: B 构造函数和析构函数
查看>>