文章目录
一、概念
1. 概念
synchronized是java中的关键字,可以在需要线程安全的业务场景中进行使用,保证线程安全,它是利用锁机制来实现同步的。
2. 特性
-
原子性:同一时间只允许一个线程持有某个对象锁,对需同步的代码块(复合操作)进行访问,是一种排他的机制,因此被synchronized关键字修饰的同步代码块是无法被中途打断的,能保证代码的原子性
-
可见性:synchronized关键字包括monitor enter和monitor exit两个JVM命令,它能保证在任何时候任何线程执行到monitor enter成功之前都必须从主内存中获取数据,而不是从缓存中,在monitor exit运行成功之后,共享变量被更新后的值必须刷入主内存。
-
有序性:synchronized的指令严格遵守java happens-before规则,一个monitor exit指令之前必定要有一个monitor enter;这种顺序性是以程序的串行化执行换来的,在synchronized关键字所修饰的代码块中代码指令也会发生指令重排序的情况
二、用法分类
1. 按照修饰对象
- 修饰代码块
- synchronized(this|object) {}
- synchronized(类.class) {}
- 修饰方法
- 修饰非静态方法
- 修饰静态方法
2. 按照获取的锁分类
- 获取对象锁
- synchronized(this|object) {}
- 修饰非静态方法
- 获取类锁
- synchronized(类.class) {}
- 修饰静态方法
- 对象锁与类锁的区别
- 对象锁:每个实例都会有一个monitor对象,即Java对象的锁,类的对象可以有多个,所以每个对象有其独立的对象锁,互不干扰
- 类锁:每个类只有一个Class对象,所以每个类只有一个类锁;类锁是加载类上的,而类信息是存在JVM方法区的,并且整个JVM只有一份,方法区又是所有线程共享的,所以类锁是所有线程共享的
三、代码实战
1. 锁住实体中的非静态变量(对象锁)
@Slf4j
public class SyncThread implements Runnable {
private final Object lock = new Object();
@Override
public void run() {
syncMethod();
}
private void syncMethod() {
log.info("thread in");
synchronized (lock) {
try {
log.info("thread start");
TimeUnit.SECONDS.sleep(2);
log.info("thread end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试代码
public class SyncTest {
public static void main(String[] args) {
SyncThread syncThread = new SyncThread();
new Thread(new SyncThread(), "thread-1").start();
new Thread(new SyncThread(), "thread-2").start();
new Thread(syncThread, "thread-3").start();
new Thread(syncThread, "thread-4").start();
}
}
运行结果
结论:如果是同一个实例,就会按顺序访问,如果是不同实例,就可以同时访问
2. 锁住实体中的静态变量(类锁)
@Slf4j
public class SyncThread implements Runnable {
private static final Object lock = new Object();
@Override
public void run() {
syncMethod();
}
private void syncMethod() {
log.info("thread in");
synchronized (lock) {
try {
log.info("thread start");
TimeUnit.SECONDS.sleep(2);
log.info("thread end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果
结论:所有实例按照顺序访问
3. 锁住无关对象
@Slf4j
public class SyncThread implements Runnable {
@Override
public void run() {
syncMethod();
}
private void syncMethod() {
log.info("thread in");
synchronized (new SyncThread()) {
try {
log.info("thread start");
TimeUnit.SECONDS.sleep(2);
log.info("thread end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果
结论:所有实例同时访问
4. 锁住this对象(对象锁)
@Slf4j
public class SyncThread implements Runnable {
@Override
public void run() {
syncMethod();
}
private void syncMethod() {
log.info("thread in");
synchronized (this) {
try {
log.info("thread start");
TimeUnit.SECONDS.sleep(2);
log.info("thread end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果
结论:如果是同一个实例,就会按顺序访问,如果是不同实例,就可以同时访问
5. 锁住类对象(类锁)
@Slf4j
public class SyncThread implements Runnable {
@Override
public void run() {
syncMethod();
}
private void syncMethod() {
log.info("thread in");
synchronized (SyncThread.class) {
try {
log.info("thread start");
TimeUnit.SECONDS.sleep(2);
log.info("thread end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
执行结果
结论:所有实例按照顺序访问
6. 锁住非静态方法(对象锁)
@Slf4j
public class SyncThread implements Runnable {
@Override
public void run() {
syncMethod();
}
private synchronized void syncMethod() {
log.info("thread in");
try {
log.info("thread start");
TimeUnit.SECONDS.sleep(2);
log.info("thread end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果
结论:如果是同一个实例,就会按顺序访问,如果是不同实例,就可以同时访问
7. 锁住静态方法(类锁)
@Slf4j
public class SyncThread implements Runnable {
@Override
public void run() {
syncMethod();
}
private synchronized static void syncMethod() {
log.info("thread in");
try {
log.info("thread start");
TimeUnit.SECONDS.sleep(2);
log.info("thread end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果
结论:所有实例按照顺序访问