让你读懂synchronized的原理
Posted chalice
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了让你读懂synchronized的原理相关的知识,希望对你有一定的参考价值。
相信熟悉java的同学对synchronized关键字也是非常熟悉了,似乎只要在涉及到线程安全的问题的问题中,加上synchronized关键字就对了!
比如下面这个我们比较常见的代码,懒汉式单例模式:
public class LazySimpleSinglethon private static LazySimpleSinglethon singlethon = null; private LazySimpleSinglethon() public synchronized tatic LazySimpleSinglethon getInstance() if(singlethon==null) //如果不加synchronizde会存在线程安全问题 singlethon = new LazySimpleSinglethon(); return singlethon;
好,问题来了,为什么会有线程安全问题?什么是线程安全问题?《Java Concurrency In Practice》一书的作者Brian Goetz 对“线程安全“有一个比较恰到的定义:“当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象就是线程安全的。” 看完这一段定义,我们可以理解为,当我们在编写代码的时候不需要考虑线程问题即不加上同步处理,程序的运行结果仍然会是正常的,那我们可以说这是线程安全的。比如上面这段代码,如果我们不加上synchronized关键字,那么在多线程环境下,很有可能会创建多个单例对象,很显然这不是我们想要的,也违背了单例模式的设计原则。
线程安全问题,说白了就是多个线程在操作共享数据时引发的数据安全问题。
在java语言中,使用synchronized关键字是我们使用互斥同步的方式(还有其他的方式保证线程安全,日后再聊)来保证线程安全的常用手段,那是不是只要涉及线程安全问题,我们都可以用synchronize来解决呢?使用synchronized关键字会不会对性能有什么影响?使用synchronized关键字的缺陷又在哪里呢?接下来我们就好好的聊一聊,synchronized关键字。
- synchrozized的用法:
- 修饰实例方法,作用于当前实例加锁,进入同步代码块前要获得当前实例的锁。
- 修饰静态方法,作用于当前类加锁,进入同步代码前要获得当前类对象的锁。
- 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码块前要获得给定对象的锁。
简单的示例代码:
public class SyncDemo //修饰实例方法 public synchronized void syncMethod() //对象锁 public void syncCode() //修饰代码块 synchronized (this) // 括号表示作用范围 this是对象级别 SyncDemo.class类级别 //保护存在线程安全的变量 //修饰静态方法 public synchronized static void syncStaticMethod() //类锁 //TODO
以上代码表示synchronized关键字的两种作用范围,一种作用于对象,一种作用于类。观察synchronized 的整个语法发现,synchronized(lock)是基于lock 这个对象的生命周期来控制锁粒度的,如果这个对象是类,那么作用范围是类级别,如果是对象,那么作用范围就是对象。显而易见类级别的范围要大于对象级别(类的生命周期>对象的生命周期),锁的级别越小,对程序性能的影响也越小。
synchronized 关键字经过编译之后,会在同步块的前后分别形成monitorenter和monitorexit这两个字节码指令,这两个字节码都需要- 个reference类型的参数来指明要锁定和解锁的对象。如果Java程序中的synchronized明确指定了对象参数,那就是这个对象的reference ;如果没有明确指定,那就根据synchronized修饰的是实例方法还是类方法,去取对应的对象实例或Class对象来作为锁对象。
以上是关于让你读懂synchronized的原理的主要内容,如果未能解决你的问题,请参考以下文章