synchronized关键字的作用是能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果。
synchronized是Java的关键字,被Java语言原生支持;synchronized是最基本的互斥同步手段。
synchronized基础
什么是多线程安全问题?
多个线程共享同一数据,当某一线程执行多条语句时,其他线程也在执行同样的语句,导致数据在某一语句中被多次修改,执行到下一语句时,导致错误数据的产生。线程不安全的产生,是由于多个线程共享内存空间;多条语句操作同一数据。
synchronized的两个用法:
- 对象锁:包括方法锁(synchronized修饰普通方法,默认锁对象为this当前实例对象)和同步代码块锁(自己指定对象)
- 类锁:指synchronized修饰静态的方法或指定锁为class对象。
对象锁
代码块形式:手动指定锁对象。
方法锁形式:synchronized修饰普通方法,锁对象默认为this对象。
什么情况下需要自定义锁对象?
类锁
Java类可以有很多个对象,但只有1个class对象。 所谓类锁,不过是class对象的锁而已。类锁在同一个时刻只能被一个对象拥有。
形式1:synchronized加在static方法上;
形式2:synchornized(*.class)代码块。
多线程访问同步方法的7种情况
两个线程同时访问一个对象的同步方法
这两个线程属于同一个对象,使用this对象作为锁,因此,这两个线程串行执行。
两个线程访问的是两个对象的同步方法
这两个线程属于不同的对象,使用的锁对象是各自的this对象(是不同的),因此,这两个线程并行执行。
两个线程访问的是静态方法
这里的锁对象是class对象,这两个线程会串行执行。
同时访问同步方法和非同步方法
非同步方法不受影响。
访问同一个对象的不同的普通同步方法
两个普通同步方法中使用的都是this对象作为锁对象,所以,必须这两个普通同步方法必须串行执行。
同时访问静态synchronized和非静态synchronized方法
静态同步方法使用的锁对象是class对象,而非静态同步方法使用的锁对象是该非静态同步方法的对象this。
方法抛出异常后,会释放锁吗?
方法抛出异常后,会释放锁。
总结:
- 一把锁同时只能被一个线程获取,没有拿到锁的线程必须等待(对应第1,5种情况)
- 每个实例都对应自己的一把锁,不同实例之间互不影响;但是,锁对象是*.class以及synchronized修饰的是static方法的时候,所有对象共用同一把类锁(对应第2,3,4,5,6种情况)
- 无论是方法正常执行完毕或者方法抛出异常,都会释放锁(对应第7种情况)
在一个被synchronized修饰的方法中调用另一个未被synchronized修饰的方法,这时是否是线程安全的?答案是:不是,因为,一旦离开本方法,进入另一个方法(而该方法未被synchronized修饰,那么这个未被修饰的方法可以同时被多个线程访问)。