一文解决synchronized
参考:Java高并发之魂:synchronized深度解析(_悟空_)
视频链接:https://pan.baidu.com/s/11XPhVBI6MNHgAjSkbq11ug
提取码:qkwu
代码:https://github.com/ouyangxizhu/concurrency_demo.git
文章目录
- 简述
- 一、synchronized作用
- 二、问题引入
- 三、synchronized的两个用法
- 阅读到这里,你可以解决文章最开始的“问题引入”提到的问题了(i++的问题)
- 四、多线程访问同步方法的7中情况(面试常考)
- 1. 两个线程同时访问一个对象的同步方法
- 2. 两个线程访问两个对象的同步方法
- 3. 两个线程访问synchronized的静态方法
- 4. 同时访问同步方法和非同步方法
- 5. 访问同一个对象的不同的普通同步方法
- 6. 同时访问静态的synchronized方法和非静态的synchronized方法
- 7. 方法抛出异常后会释放锁
- 五、总结
- 六、补充
- 七、性质
- 八、原理
- 九、深入JVM看字节码monitor指令
- 十、可见性
- 十一、缺陷
- 十二、synchronized使用注意事项
- 十三、如何选择Lock和synchronized
- 十四、思考题
- 1. 多个线程等待同一个synchronized锁的时候,JVM如何选择下一个获取锁的线程。(什么算法?一直等待的?还是刚刚来的线程)
- 2. synchronized使得同时只有一个线程可以执行,性能较差,有什么办法可以提升性能?
- 3. 我想更加灵活的控制获取和释放(现在释放锁的时机已经被规定死了)
- 4. 什么是锁的升级和降级,什么是JVM的偏向锁,轻量级锁,重量级锁
- 十五、总结
简述
synchronized是java的关键字,是java语言原生支持的,是最基本的互斥同步手段。
一、synchronized作用
能够保证同一时刻最多只有一个线程执行该代码,以达到保证并发安全的效果。
二、问题引入
两个线程同时执行a++,最后结果会比预计的少。(synchronized_demo->DisappearRequest1)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 消失的请求
*/
public class DisappearRequest1 implements Runnable{
static DisappearRequest1 instance = new DisappearRequest1();
static int i = 0;
public static void main(String[] args){
try {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
t1.join();
t2.join();//join方法保证该线程的方法执行完之后才能执行下面的代码。
System.out.println(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void run() {
for(int j = 0; j < 100000; j++)
i++;
}
}
运行结果
这个结果小于200000(不论运行多少次)。
原因
因为i++这个操作不是原子操作。具体的解释如下:
i++其实是三个操作
- 线程读取i的值。int temp = i;
- 将该值+1。temp = temp + 1;(其实这的表述有问题,temp = temp + 1也不是原子操作其实等同于i++。只是用这个表示将该值+1,即假设是原子操作+1)。
- 将该值写入到内存。i = temp;
具体解释是这样的: - 假设t1线程读取i的值为0。
- 这时t2线程读取i的值也为0;(因为t1线程还没修改或者修改后还没写入到内存)
- t1和t2线程分别执行+1操作,再写入内存。
执行结果就是少加了一次1。这种情况导致最后的结果小于200000。
三、synchronized的两个用法
注在本小结的阅读中,读者要是有问题或者想到其他形式(但是不知道运行结果),读者可以自己尝试或者文章后面会有全部形式的例子
1. 对象锁
1) 方法锁(synchronized修饰非静态方法)
默认锁对象为this,即当前实例对象。(synchronized_demo->SynchronizedObjectMethod2)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 对象锁实例,普通方法锁
*/
public class SynchronizedObjectMethod2 implements Runnable{
static SynchronizedObjectMethod2 instance = new SynchronizedObjectMethod2();
@Override
public void run() {
method();
}
public synchronized void method(){
System.out.println("我是对象锁的方法修饰符形式,我叫" + 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();
while (t1.isAlive() || t2.isAlive());//空循环
System.out.println("finished");
}
}
运行结果
结果为顺序执行。
2) 同步代码块锁
自己指定锁对象。(synchronized_demo->SynchronizedObjectCodeBlock3)
package synchronized_demo;
/**
-
Created by ouyangxizhu on 2019/5/15.
-
对象锁 代码块形式
*/
public class SynchronizedObjectCodeBlock3 implements Runnable{
static SynchronizedObjectCodeBlock3 instance = new SynchronizedObjectCodeBlock3();
@Override
public void run() {
synchronized (this) {
System.out.println(“我是对象锁的代码块形式,我是:” + 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();
while (t1.isAlive() || t2.isAlive());//空循环
System.out.println(“finished”);
}
}
运行结果
结果为顺序执行。
2. 类锁
1)syncronized修饰静态方法。
(synchronized_demo->SynchronizedClassStatic4)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 类锁 synchronized修饰静态方法形式
*/
public class SynchronizedClassStatic4 implements Runnable {
static SynchronizedClassStatic4 instance1 = new SynchronizedClassStatic4();
static SynchronizedClassStatic4 instance2 = new SynchronizedClassStatic4();
@Override
public void run() {
method();
}
public static synchronized void method(){
System.out.println("我是类锁的synchronized修饰静态方法形式,我叫" + 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(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive()) ;//空循环
System.out.println("finished");
}
}
运行结果
顺序执行。
2)指定锁为Class对象
java类可以有很多对象,但是只有一个类(Class)对象,即synchronized(*.class)代码块形式。其实是Class对象的锁而已,只不过只有一个而已。(synchronized_demo->SynchronizedClassClass5)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 类锁 synchronized(*.class)形式
*/
public class SynchronizedClassClass5 implements Runnable {
static SynchronizedClassClass5 instance1 = new SynchronizedClassClass5();
static SynchronizedClassClass5 instance2 = new SynchronizedClassClass5();
@Override
public void run() {
method();
}
public synchronized void method(){
synchronized (SynchronizedClassClass5.class) {
System.out.println("我是对象锁的synchronized(*.class)形式,我是:" + 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(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive()) ;//空循环
System.out.println("finished");
}
}
运行结果
顺序执行。
阅读到这里,你可以解决文章最开始的“问题引入”提到的问题了(i++的问题)
四、多线程访问同步方法的7中情况(面试常考)
1. 两个线程同时访问一个对象的同步方法
(synchronized_demo->SynchronizedStyle1)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 两个线程同时访问一个对象的同步方法
*/
public class SynchronizedStyle1 implements Runnable{
static SynchronizedStyle1 instance = new SynchronizedStyle1();
@Override
public void run() {
method();
}
public synchronized void method(){
System.out.println("我是两个线程同时访问一个对象的同步方法形式,我叫" + 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();
while (t1.isAlive() || t2.isAlive());//空循环
System.out.println("finished");
}
}
运行结果:
顺序执行
2. 两个线程访问两个对象的同步方法
(synchronized_demo->SynchronizedStyle2)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 两个线程访问两个对象的同步方法
*/
public class SynchronizedStyle2 implements Runnable{
static SynchronizedStyle2 instance1 = new SynchronizedStyle2();
static SynchronizedStyle2 instance2 = new SynchronizedStyle2();
@Override
public void run() {
synchronized (this) {
System.out.println("我是两个线程访问两个对象的同步方法形式,我是:" + 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(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive());//空循环
System.out.println("finished");
}
}
运行结果:
或者
注: 这两个结果表示,线程可以先运行Thread-0也可以先运行Thread-1。
3. 两个线程访问synchronized的静态方法
(synchronized_demo->SynchronizedStyle3)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 两个线程访问synchronized的静态方法
*/
public class SynchronizedStyle3 implements Runnable {
static SynchronizedStyle3 instance1 = new SynchronizedStyle3();
static SynchronizedStyle3 instance2 = new SynchronizedStyle3();
@Override
public void run() {
method();
}
public static synchronized void method(){
System.out.println("我是两个线程访问synchronized的静态方法形式,我叫" + 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(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive()) ;//空循环
System.out.println("finished");
}
}
4. 同时访问同步方法和非同步方法
(synchronized_demo->SynchronizedStyle4)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 同时访问同步方法和非同步方法
*/
public class SynchronizedStyle4 implements Runnable {
static SynchronizedStyle4 instance = new SynchronizedStyle4();
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0"))
method1();
else
method2();
}
public synchronized void method1(){
System.out.println("我是加锁形式,我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public void method2(){
System.out.println("我是不加锁形式,我叫" + 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();
while (t1.isAlive() || t2.isAlive()) ;//空循环
System.out.println("finished");
}
}
5. 访问同一个对象的不同的普通同步方法
(synchronized_demo->SynchronizedStyle5)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 访问同一个对象的不同的普通同步方法
*/
public class SynchronizedStyle5 implements Runnable {
static SynchronizedStyle5 instance = new SynchronizedStyle5();
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0"))
method1();
else
method2();
}
public 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() + "运行结束");
}
public 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() + "运行结束");
}
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("finished");
}
}
或者
6. 同时访问静态的synchronized方法和非静态的synchronized方法
(synchronized_demo->SynchronizedStyle6)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 同时访问静态的synchronized方法和非静态的synchronized方法
*/
public class SynchronizedStyle6 implements Runnable {
static SynchronizedStyle6 instance = new SynchronizedStyle6();
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0"))
method1();
else
method2();
}
public static 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() + "运行结束");
}
public 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() + "运行结束");
}
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("finished");
}
}
一个是类锁,一个是对象锁,锁不一样,所以有这种结果。
7. 方法抛出异常后会释放锁
(synchronized_demo->SynchronizedStyle7)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 方法抛出异常后会释放锁
* 一旦第一个线程抛出异常,第二个线程会立刻获得锁
*/
public class SynchronizedStyle7 implements Runnable {
static SynchronizedStyle7 instance = new SynchronizedStyle7();
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0"))
method1();
else
method2();
}
public synchronized void method1(){
System.out.println("我是非静态加锁method1,我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
throw new RuntimeException();
}
public 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() + "运行结束");
}
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("finished");
}
}
五、总结
- 一个锁只能同时被一个线程获取,没有拿到锁的线程必须等待(对应第1,5的情况)。
- 每个实例都对应自己的一把锁,不同实例之间互不影响;例外:当锁对象是*.class或者synchronized修饰静态方法时所有对象获得的锁是同一把锁(类锁,该类只有一个类对象)。(对应2,3,4,6)
- 方法无论是正常执行完毕或者方法抛出异常,都会释放锁(对应第7种情况)。
六、补充
在一个同步方法中调用非同步方法时不是线程安全的(非同步方法不是线程安全,可以同时访问)。
七、性质
1. 可重入
同一线程的外层函数获得锁之后,内层函数可以直接获得该锁。
好处
1)避免死锁
比如:假设一个类里面有两个同步方法,第一个方法里面有第二个同步方法的调用。如果一个实例对象的线程想执行第一个同步方法,当执行第一个方法里面的第二个方法的调用的时候会出现死锁。(执行第一个同步方法时获得了该实例对象的锁,当在该方法中调用第二个方法时,必须获得该实例对象锁,但是该锁已经被该实例对象持有。如果不可重入,会永远等待,产生死锁,第二个方法不会被执行)。
2)提升封装性
避免一次次解锁加锁,简化编程。
粒度
线程而非调用。
证明同一个方法是可重入的
(synchronized_demo->SynchronizedRecursion1)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 粒度证明 证明同一个方法是可重入的(递归调用本身)
*/
public class SynchronizedRecursion1 {
int a = 0;
public synchronized void method1() {
System.out.println("我是method1,a = " + a);
if (a == 0) {
a++;
method1();
}
}
public static void main(String[] args) {
SynchronizedRecursion1 synchronizedRecursion1 = new SynchronizedRecursion1();
synchronizedRecursion1.method1();
System.out.println("finished");
}
}
证明可重入不要求是同一个方法
(synchronized_demo->SynchronizedRecursion2)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 粒度证明 证明可重入不要求是同一个方法 调用类内另外的方法
*/
public class SynchronizedRecursion2 {
public synchronized void method1() {
System.out.println("我是method1" );
method2();
}
public synchronized void method2() {
System.out.println("我是method2" );
}
public static void main(String[] args) {
SynchronizedRecursion2 synchronizedRecursion1 = new SynchronizedRecursion2();
synchronizedRecursion1.method1();
System.out.println("finished");
}
}
证明可重入不要求是同一个类中的
(synchronized_demo->SynchronizedRecursion3)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 粒度证明 证明可重入不要求是同一个类中的 调用父类的方法
*/
public class SynchronizedRecursion3 {
public synchronized void doSomething() {
System.out.println("我是父类的方法" );
}
}
class TestClass extends SynchronizedRecursion3{
public synchronized void doSomething() {
System.out.println("我是子类的方法" );
super.doSomething();
}
public static void main(String[] args) {
TestClass s = new TestClass();
s.doSomething();
System.out.println("finished");
}
}
这个例子我觉得写的不好。自己写了一个
(synchronized_demo->SynchronizedRecursion3_1)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
*/
public class SynchronizedRecursion3_1 {
static TestSyn t = new TestSyn();
public static void method(){
synchronized(t.getClass()){
System.out.println("我是SynchronizedRecursion3_1的method方法");
}
}
}
class TestSyn{
public static void main(String[] args){
method();
}
public synchronized static void method(){
System.out.println("我是TestSyn的method方法");
SynchronizedRecursion3_1.method();
}
}
2. 不可中断
一旦这个锁已经被别的线程获得了,如果本线程还想获得,只能选择等待或者阻塞,直到别的线程释放这个锁。如果别的线程永远不释放,本线程永远拿不到。
Lock类有中断的能力,第一点,可以中断现在已经获得锁的线程的执行。第二点如果等待时间太长,可以退出。
八、原理
现象
当前线程要执行同步代码或者同步代码块时,当前线程必须获得指定对象锁,当前线程获得之后别的线程(想要执行该方法或者代码块)都要等待,直到当前线程执行完同步方法或者同步代码块,或者发生异常退出才会释放锁。
等价代码
(synchronized_demo->SynchcronizedToLock)
package synchronized_demo;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created by ouyangxizhu on 2019/5/15.
*/
public class SynchcronizedToLock {
Lock lock = new ReentrantLock();
public synchronized void method1(){
System.out.println("我是synchronized形式的锁");
}
public void method2(){
try {
lock.lock();
System.out.println("我是Lock形式的锁");
} finally {
lock.unlock();
}
}
public static void main(String[] args){
SynchcronizedToLock s = new SynchcronizedToLock();
s.method1();
s.method2();
}
}
上面method1和metho2等价
九、深入JVM看字节码monitor指令
(synchronized_demo->SynchronizedDecompilation)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 反编译字节码
*/
public class SynchronizedDecompilation {
private Object object = new Object();
public void insert(Thread thread){
synchronized (object){
}
}
}
我用的是windows系统,不同系统的指令不一样
- 打开cmd
- 输入d:
- 进入类所在路径 cd D:\Chengxu\Myeclipsechengxu\concurrency_demo\src\synchronized_demo
- 编译 javac SynchronizedDecompilation.java
- 反编译javap -verbose SynchronizedDecompilation.class
- 之后可以看到下面的内容
monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM要保证每个monitorenter必须有对应的monitorexit与之配对。任何对象都有一个monitor(存在对象头中)与之关联,当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁。
当一个线程想获得锁时(monitorenter指令)有三种情况
1).monitor计数器为0,该线程会获得锁,之后计数器加一,之后的线程就没办法获得锁了。
2) 如果已经获得锁了,再一次进入同步代码块(可重入性质),计数器会递增。
3)monitor被其他线程获得,本线程阻塞,直到别的线程释放锁。
当一个线程执行monitorexit指令
该指令会使计数器减一。计数为零时锁被释放。
十、可见性
每一个线程会复制主内存的副本(其实是个抽象的概念,真实是放在缓冲区当中的),当线程之间通讯时,是通过先写入主内存,再从主内存当中读出来实现的。
当执行synchronized的同步方法时,在执行之前会从主内存读,执行完毕之前(释放锁之前)会强制写入主内存。
十一、缺陷
1. 效率低
1)锁的释放情况少
方法执行完毕后或者发生异常。(当长时间I/O等情况时,别的线程只能等待)
2)试图获得锁不能设定超时、不能中断一个试图获得锁的线程(Lock类可以)。
2. 不够灵活(读写锁更灵活)
加锁和释放的时机单一,每个锁仅有单一的条件,可能是不够的,对比读写锁(其实读的时候不用加锁)
3. 无法知道是否成功获得锁
(synchronized_demo->LockExample)
package synchronized_demo;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Created by ouyangxizhu on 2019/5/15.
*/
public class LockExample {
public static void main(String[] args){
ReentrantLock lock = new ReentrantLock();
lock.lock();//锁住
lock.unlock();//释放
boolean b1 = lock.tryLock();//可以知道是否可以获得锁
boolean b2 = lock.tryLock(3000, TimeUnit.SECONDS);//可以设置超时时间
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();//读写锁
reentrantReadWriteLock.readLock();//读锁
reentrantReadWriteLock.writeLock();//写锁
}
}
十二、synchronized使用注意事项
1. 锁对象不能为空
monitor存储在对象头当中,没有对象当然就没有monitor。
2. 作用域不宜过大
将大部分代码都放到synchronized中会使大部分代码串行工作,影响速度。
3. 避免死锁
避免以下类型代码
synchronized (instance1) {
synchronized (instance2){
...
}
}
synchronized (instance2) {
synchronized (instance1){
...
}
}
十三、如何选择Lock和synchronized
1. 尽量都不使用,尽量使用JUC包下的类(使用CAS实现)
2. 尽量使用synchronized,因为可以少写代码。
3. 使用Lock的特性时才用Lock
注以上都是为了尽量少出错。使用更简单的,防止复杂。
十四、思考题
1. 多个线程等待同一个synchronized锁的时候,JVM如何选择下一个获取锁的线程。(什么算法?一直等待的?还是刚刚来的线程)
主要看算法,不同版本的JVM不一样(比如等待时间最长的?刚进来的?随机的?)(公平锁 非公平锁)
2. synchronized使得同时只有一个线程可以执行,性能较差,有什么办法可以提升性能?
这个需要看业务
1)synchronized尽量少的包裹代码块
2)可以使用读写锁(防止读的时候加锁)
3. 我想更加灵活的控制获取和释放(现在释放锁的时机已经被规定死了)
可以自己实现接口
4. 什么是锁的升级和降级,什么是JVM的偏向锁,轻量级锁,重量级锁
1)锁只能升级不能降级。
2)偏向锁,轻量级锁,重量级锁是为了优化synchronized关键字实现的,与对象头的标记有关。这里简单介绍一下:”
偏向锁主要解决不需要加锁的情况,即只有一个线程执行或者不竞争
轻量级锁解决少量竞争的情况(才用自旋锁)
十五、总结
一句话概括synchronized
JVM会自动通过使用monitor来加锁和解锁,保证了同时只有一个线程可以执行指定代码,从而保证了线程安全,同时具有可重入和不可中断的性质。