一、锁
锁是一种互斥的机制,在多线程环境中实现对资源的协调与控制,凡是有资源被多线程共享,涉及到修改的情况就要考虑锁的加持。
(0)Java锁原理
0)引申:Java对象结构
Java对象结构分为3部分:
①对象头(包括:Mark Word(存储了当前对象运行时的状态信息,如HashCode、指向锁记录的指针等)、Class Pointer(指针,指向当前对象类型所在方法区中的Class信息));
如图,MarkWord结构(jdk1.8)
HotSpot 64位操作系统(一个对象的markWord在内存占用8字节)
锁标志位,分别代表无锁、偏向锁、轻量级锁、重量级锁4种状态;
②实例数据;
③对齐填充字节(在内存中占8字节)。
1)在Java中,每个对象都拥有一把锁,存放在对象头中,记录了当前对象被哪个线程占用。
2)操作系统用户态和内核态
由于需要限制不同的程序之间的访问能力,防止他们获取别的程序的内存数据,或者获取外围设备的数据,并发送到网络,CPU划分出两个权限等级,用户态和内核态。所有用户程序都是运行在用户态的,当程序需要做一些内核态的事情,,例如从硬盘读取数据,,或者从键盘获取输入等。而唯一可以做这些事情的就是操作系统,此时程序就需要操作系统请求以程序的名义来执行这些操作,即将用户态程序切换到内核态。
内核态: CPU可以访问内存所有数据,,包括外围设备, 例如硬盘,、网卡, CPU也可以将自己从一个程序切换到另一个程序。
用户态: 只能受限的访问内存,且不允许访问外围设备.,占用CPU的能力被剥夺。
(1)锁的实现方式
0)引申:
在java中,锁的实现主要采用两种方式:1、基于Object的悲观锁;2、基于CAS的乐观锁,Lock接口是基于CAS原理实现。java5之前的版本只有synchronized锁,基于操作系统提供的指令,在内核态实现多线程之间访问资源的同步性;之后发现基于内核态的synchronize的锁开销很大,提出了Lock锁机制,在java5版本中被官方采纳;随后java官方对synchronized进行了优化,提出了对象锁的4种状态概念。在java的后续版本中,两者在性能上差别需要根据实际情况进行选择使用。
1)synchronized
j.u.c.Locks中说明synchronized是在硬件层面依赖特殊的CPU指令。synchronized别编译后会生成monitorenter和monitorexit两个字节码指令,依赖这两个字节码指令进行线程同步。monitor,监视器(管程),一旦线程进入了monitor,那么其他线程只能等待,只有当这个线程退出,其他线程才有机会进入。monitor依赖于操作系统的Mutex Lock实现,所以每当挂起或唤醒线程,都要切换到操作系统的内核态,这个操作比较重量级。在某些情况下,甚至于切换时间本身就会超出线程执行任务的时间。java6开始,对synchronized进行了优化,引入了对象锁的4种状态,分别是无锁、偏向锁、轻量级锁、重量级锁。
eg:
public class SynchronizedDemo {
public void method() {
synchronized (this) {
System.out.println("Method 1 start");
}
}
}
对以上示例代码执行javap -p -c 指令(说明,javap <---> java class文件分解器, -p <-----> 展示所有的类和成员, -c <-------> 对代码进行反编译, 具体指令说明可通过javap -help 展示)
synchronized特点:
①支持线程可重入;
②等待状态(前一个线程并未释放锁,当前线程处于不断尝试获取锁的状态(对应java中定义的RUNNABLE状态))不可中断;
③synchronized会自动释放锁;
④synchronized是非公平锁;
⑤synchronized既可以锁住代码块,也可以锁住方法;
注:java中定义了线程执行的的6种状态
1.创建 2. 执行 3.销毁 4.时间限制的等待 5.无线等待 6.阻塞
操作系统中定义的线程状态有3种:运行态、阻塞态、就绪态;线程的生命周期在此基础上添加了创建和销毁;
2)Lock接口
Lock接口提供了区别于synchronized的另一种具有方法操作的同步方式,支持更多灵活的结构,可以关联多个Condition(java提供的用户线程通信的接口)对象。
package lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class RetreenLockTest {
private static Lock rl = new ReentrantLock();
private static void testReentrantLock(){
Runnable run = ()->{