一、synchronized的使用
二、synchronized的实现原理
三、锁的升级过程
一、synchronized的使用
synchronized是Java提供的用于线程同步的锁机制,使用synchronized关键字可以有效解决线程同步的原子性和可见性问题。
原子性是指保证某些临界代码只能同时由一个线程执行
可见性是指线程对共享资源的修改可以及时让其他线程见到(即刷新值回主存中,并通知其他线程)
1、synchronized的用法:
1.1修饰代码块(锁当前对象,调用该方法的对象会获得锁)
1.2修饰实例方法(锁当前对象,调用该方法的对象会获得锁)
1.3修饰静态方法(锁的是该类对象:类名.class;调用该类的对象会获得锁)
1.4修饰类(锁的是该类对象:类名.class,调用该类的对象会获得锁)
package com.djn.Thread;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SynchronizedDemo {
private static int count=0;
private static final int THREAD_NUMBER=5; //线程数量
private static final int REQUEST_NUMBER=100; //模拟并发数量
public void add() {
//1.修饰代码块
synchronized (this) {
count++;
}
}
public synchronized void add1() {
//2.修饰方法
count++;
}
public static synchronized void add2() {
//3.修饰静态方法
count++;
}
public void add3() {
//4.修饰类
synchronized (this.getClass()) {
count++;
}
}
public static void main(String[] args) {
/*
* synchronized的四种用法
* 1、修饰代码块(锁当前this对象)
* 2、修饰实例方法(锁当前this对象)
* 3、修饰静态方法(锁类对象:类名.class)
* 4、修饰类(锁类对象:类名.class)
*/
SynchronizedDemo synchronizedDemo = new SynchronizedDemo();
ExecutorService executorService = Executors.newFixedThreadPool(THREAD_NUMBER);
CountDownLatch countDownLatch = new CountDownLatch(REQUEST_NUMBER);
for(int i=1; i<=REQUEST_NUMBER; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
synchronizedDemo.add();
countDownLatch.countDown();
}
});
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
executorService.shutdown();
System.out.println(count);
}
}
}
2、synchronized保证可见性的原因
因为在线程获取锁时(即进入临界区时),会强制到内存当中获取最新的共享数据,当释放锁时,会将修改后的值重新刷新到内存当中。
二、synchronized的实现原理
那么synchronized的底层是怎么实现的呢?
我们先来了解一下java对象的内存布局(java object layout 即jol)
markword:标记对象的锁状态等(属于对象头:gc、hashcode和锁信息
类元信息指针:指向方法区中类元信息的指针
实例数据:存放对象的成员变量
对齐填充:将对象的字节数补充到8的整数倍
当使用synchronized给对象上锁时,就是将当前调用该方法的线程的信息存入到对象的markword当中,当有另外一个线程需要调用这个对象的方法时,如果该对象markword中已经有其他线程信息,则这个线程会停下来。这就是锁的原理,依赖于jvm。
现在看synchronized的四种用法,实际上只有两种,一种是锁实例对象的markword(对该实例对象有效),一种是锁类对象(对所有实现该类的对象有效)
三、锁升级的过程
知道了synchronized的锁原理,现在我们来聊一下锁升级过程
偏向锁:就是在对象的markword上标记了某一线程(即偏向于这个线程)
轻量级锁:即自旋锁(CAS),当有多个线程竞争锁时,一个线程获得锁,其他线程处于自旋状态
重量级锁:当有多个线程获得锁,一个线程获得锁,其他线程放入阻塞队列当中
匿名偏向:在新建对象之前,jvm的偏向锁已经启动(默认启动,但会有延迟,一般会在jvm启动4秒之后启动偏向锁),这时候新建对象则会直接为该对象上偏向锁。
在控制台上输入
java -XX:+PrintFlagsFinal -version
查看jvm的参数
锁的升级过程:
当只有一个线程访问synchronized时,会在该对象的markword上标记当前线程,成为偏向锁,当再有线程访问时,两个线程会竞争markword上的位置,这时候便成为自旋锁(即轻量锁),当并发数量很多的时候,便会成为重量级锁。