深入理解synchronized锁
synchronized的特性
1、原子性:所谓原子性就是指一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
2、可见性:可见性是指多个线程访问一个资源时,该资源的状态、值信息等对于其他线程都是可见的。
3、有序性:有序性值程序执行的顺序按照代码先后执行。
4、可重入性:当一个线程试图操作一个由其他线程持有的对象锁的临界资源时,将会处于阻塞状态,但当一个线程再次请求自己持有对象锁的临界资源时,这种情况属于重入锁。通俗一点讲就是说一个线程拥有了锁仍然还可以重复申请锁。
synchronized的案例
synchronized可以修饰静态方法、成员函数,同时还可以直接定义代码块,但是归根结底它上锁的资源只有两类:一个是对象,一个是类。
package com.example.juc;
import java.util.concurrent.TimeUnit;
/**
* @author zjt
* @date 2021-03-06
*/
public class SynchronizedDemo {
public static void main(String[] args) throws InterruptedException {
Print print = new Print();
// 问题1 仅调用普通加锁方法A B 打印的顺序一定是先A后B吗?
// new Thread(print::printSyncA).start();
// new Thread(print::printSyncB).start();
// 结果
// 跟CPU的调度有关,不一定是先A后B,若计算机性能足够好,先打印A的概率大一些
// --------------
// 问题2
// 调用睡眠方法 A 与普通加锁方法B 打印的顺序是什么?
// new Thread(print::printSyncSleepA,"A").start();
// Thread.sleep(200); // 加一个睡眠时间保证线程A先启动
// new Thread(print::printSyncB,"B").start();
// 结果 一定是 先A 后B
// 原因:1、synchronized关键字修饰方法 被锁住的是当前对象 this 两个方法锁住的是同一个对象
// 2、.sleep() 方法不释放当前锁,占着锁等待 所以打印的顺序一定是 先A 后 B
// 一个资源类中,有多个 synchronized关键字修饰的方法,多线程调用时,一个方法抢占到CPU执行
// ,其它synchronized关键字修饰的方法需要等待
// --------------
// 问题3
// 调用睡眠加锁方法A 普通方法C 打印的顺序是什么?
// new Thread(print::printSyncSleepA, "A").start();
// Thread.sleep(200); // 加一个睡眠时间保证线程A先启动
// new Thread(print::printC, "C").start();
// 结果 先C 后A
// C是普通方法,没有使用synchronized关键字修饰,方法C并不需要获取锁 跟方法A各不相干
// --------------
// 问题4 新建对象print1 分别 调用睡眠加锁方法A 普通加锁方法B
// Print print1 = new Print();
// new Thread(print::printSyncSleepA, "A").start();
// Thread.sleep(200); // 加一个睡眠时间保证线程A先启动
// new Thread(print1::printSyncB, "B").start();
// 结果 先B后A
// 原因:加锁的对象不再是同一个,它们之间也是各不相干
// --------------
// 问题5 调用静态加锁方法A 静态加锁方法B
// new Thread(Print::printStaticSyncA, "A").start();
// Thread.sleep(200); // 加一个睡眠时间保证线程A先启动
// new Thread(Print::printStaticSyncB, "B").start();
// 静态方法可以直接使用类名.方法调用,锁住的对象不再是 this 而是 Print.class
// 无论在被new出多少个对象,都是同一个锁
// --------------
// 问题6 调用静态加锁方法A 普通加锁方法B
new Thread(Print::printStaticSyncA, "A").start();
Thread.sleep(200); // 加一个睡眠时间保证线程A先启动
new Thread(print::printSyncB, "B").start();
// 两块代码锁住的不是同一个对象,所以打印的顺序是 先B后A
}
}
class Print {
// 静态加锁方法打印A
public static synchronized void printStaticSyncA() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A");
}
// 静态加锁方法打印B
public static synchronized void printStaticSyncB() {
System.out.println("B");
}
// 普通加锁方法打印A
public synchronized void printSyncA() {
System.out.println("A");
}
// 普通加锁并且线程睡眠3秒方法打印A
public synchronized void printSyncSleepA() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A");
}
// 普通加锁方法打印B
public synchronized void printSyncB() {
System.out.println("B");
}
// 普通方法打印C
public void printC() {
System.out.println("C");
}
// 普通方法打印C
public void printSyncC() {
// 在同步代码块中显示声明锁的对象 在资源竞争时更加清晰一点
synchronized (this) {
System.out.println("C");
}
}
// 普通方法打印C
public void printSyncC1() {
synchronized (Print.class) {
System.out.println("C");
}
}
}
synchronized简单解释
synchronized实现同步的基础是:Java中的每一个对象都可以作为锁。
具体表现为3中方式:
1、对于普通同步方法:锁的是当前实例对象。
2、对于静态同步方法:锁的是当前类的Class对象。
3、对于同步方法块:锁的是synchronized ()
括号中显示声明的对象。
当一个线程试图访问同步代码时,首先必须获得锁,执行完成或者抛出异常时必须释放锁。也就是说一个实例对象的非静态同步代码方法获取锁后,该实例对象其它非静态同步方法必须等待获取锁的方法释放锁后,才能获取锁。同理静态方法也是如此,不过锁的是当前类的Class对象。
synchronized有两种形式上锁,一个是对方法上锁,一个是构造同步代码块。他们的底层实现其实都一样,在进入同步代码之前先获取锁,获取到锁之后锁的计数器+1,同步代码执行完锁的计数器-1,如果获取失败就阻塞式等待锁的释放。只是他们在同步块识别方式上有所不一样,从class字节码文件可以表现出来,是monitorenter和monitorexit指令操作。
public printSyncC()V
TRYCATCHBLOCK L0 L1 L2 null
TRYCATCHBLOCK L2 L3 L2 null
L4
LINENUMBER 115 L4
ALOAD 0
DUP
ASTORE 1
MONITORENTER
L0
LINENUMBER 116 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "C"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L5
LINENUMBER 117 L5
ALOAD 1
MONITOREXIT
L1
GOTO L6
L2
FRAME FULL [com/example/juc/Print java/lang/Object] [java/lang/Throwable]
ASTORE 2
ALOAD 1
MONITOREXIT
L3
ALOAD 2
ATHROW
L6
LINENUMBER 118 L6
FRAME CHOP 1
RETURN
L7
LOCALVARIABLE this Lcom/example/juc/Print; L4 L7 0
MAXSTACK = 2
MAXLOCALS = 3
我在自学时找到一篇介绍synchronized实现原理文章,写的非常不错,附上原文链接
大神,快来碗里 的synchronized实现原理