JUC: 线程锁
·
乐观锁与悲观锁
(1)悲观锁
synchronized和Lock都是悲观锁, 同一时间点,有且只有一个线程能够访问对应的资源。 写操作多的场景使用。
(2)乐观锁
认为自己在使用数据时,不会有别的线程修改数据或资源,所以不会添加锁。只是在更新资源的时候,需要去判断当前数据有没有别的线程更新过。判断规则有:
- 版本号机制version,每一次更新一个版本号。
- 采用CAS算法,Java原子类中的递增操作就通过
CAS自旋实现的。比较并交换
3 锁是什么
(1)锁案例演示 - synchronized的三种应用方式
// 1. 对象锁:对于非静态方法使用synchronized就是加的对象锁,获得的是这个对象(this)作为锁 |
public synchronized void sentEmail(){ |
try { |
TimeUnit.SECONDS.sleep(4); |
} catch (InterruptedException e) { |
throw new RuntimeException(e); |
} |
System.out.println("sent email"); |
} |
// 2. 类锁:对于静态方法或使用synchronized就是加的类锁,获得的是这个类对象(.class)作为锁 |
public static synchronized void sentSMS(){ |
System.out.println("sent SMS"); |
} |
// 3. 代码块,使用的是synchronized括号内的对象 |
public void testSynchronized(){ |
synchronized (this){ |
System.out.println("testSynchronized"); |
} |
} |
(2)从字节码角度分析synchronized实现
javap -c ***.class文件反编译
javap -c a.class # 反汇编代码,输出每个方法的 Java 字节码指令(指令集) |
-v或 -verbose # 输出最详细的附加信息,包括版本号、常量池、方法描述符(签名)、栈大小等 |
synchronized同步代码块
public void testSynchronized(){ |
synchronized (this){ |
System.out.println("testSynchronized"); |
} |
} |
public void testSynchronized(); |
Code: |
0: aload_0 |
1: dup |
2: astore_1 |
3: monitorenter # 获取锁 |
4: getstatic #26 // Field java/lang/System.out:Ljava/io/PrintStream; |
7: ldc #44 // String testSynchronized |
9: invokevirtual #34 // Method java/io/PrintStream.println:(Ljava/lang/String;)V |
12: aload_1 |
13: monitorexit # 释放锁 |
14: goto 22 |
17: astore_2 |
18: aload_1 |
19: monitorexit # 异常情况也可以释放锁 |
20: aload_2 |
21: athrow |
22: return |
Exception table: |
from to target type |
4 14 17 any |
17 20 17 any |
} |
synchronized对象锁
-v
public synchronized void sentEmail(){ |
System.out.println("sent email"); |
} |
public synchronized void sentEmail(); |
descriptor: ()V |
flags: (0x0021) ACC_PUBLIC, ACC_SYNCHRONIZED # 会检查对象的ACC_SYNCHRONIZED是否被设置,如果设置了,则会持有monitor直到方法完成释放 |
Code: |
stack=2, locals=1, args_size=1 |
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream; |
3: ldc #13 // String sent email |
5: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V |
8: return |
LineNumberTable: |
line 31: 0 |
line 32: 8 |
LocalVariableTable: |
Start Length Slot Name Signature |
0 9 0 this Lcom/thread/sgg/juc/Phone; |
synchronized类锁
public static synchronized void sentSMS(){ |
System.out.println("sent SMS"); |
} |
public static synchronized void sentSMS(); |
descriptor: ()V |
flags: (0x0029) ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED # 根据是否有ACC_STATIC是否存在判断应该是类锁还是对象锁 |
Code: |
stack=2, locals=0, args_size=0 |
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream; |
3: ldc #21 // String sent SMS |
5: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V |
8: return |
LineNumberTable: |
line 34: 0 |
line 35: 8 |
(3)反编译synchronized锁的是什么
为什么任何一个对象都可以成为一个锁?
Object是任何类的父类
什么是管程monitor?
monitor是一种程序结构,结构内的多个子程序(对象或模块)形成的多个工作线程互斥访问共享资源。
这些共享资源一般是硬件设备或一群变量。对共享变量能够进行的所有操作集中在一个模块中。(把信号量及其操作原语“封装”在一个对象内部)管程实现了在一个时间点,最多只有一个线程在执行管程的某个子程序。管程提供了一种机制,管程可以看做一个软件模块,它是将共享的变量和对于这些共享变量的操作封装起来,形成一个具有一定接口的功能模块,进程可以调用管程来实现进程级别的并发控制。
//结构体如下 |
ObjectMonitor::ObjectMonitor() { |
_header = NULL; |
_count = 0; // 该线程获取锁的次数 |
_waiters = 0, |
_recursions = 0; //线程的重入次数 |
_object = NULL; |
_owner = NULL; //标识拥有该monitor的线程 |
_WaitSet = NULL; //等待线程组成的双向循环链表,_WaitSet是第一个节点 |
_WaitSetLock = 0 ; |
_Responsible = NULL ; |
_succ = NULL ; |
_cxq = NULL ; //多线程竞争锁进入时的单向链表 |
FreeNext = NULL ; |
_EntryList = NULL ; //_owner从该双向循环链表中唤醒线程结点,_EntryList是第一个节点 |
_SpinFreq = 0 ; |
_SpinClock = 0 ; |
OwnerIsThread = 0 ; |
} |
4 公平锁与非公平锁
公平锁:多个线程按照申请锁的顺序获取锁
非公平锁:不按照顺序获取锁,可能导致某些线程处于饥饿状态
为什么有公平和非公平锁的设计?为什么默认非公平锁?
非公平锁能减少线程切换,减少CPU空闲状态的时间,效率较高。
public class ReentrantLock implements Lock, java.io.Serializable { |
abstract static class Sync extends AbstractQueuedSynchronizer { |
... |
} |
/** |
* Sync object for non-fair locks |
*/ |
static final class NonfairSync extends Sync { |
... |
} |
/** |
* Sync object for fair locks |
*/ |
static final class FairSync extends Sync { |
... |
} |
public ReentrantLock() { |
sync = new NonfairSync(); |
} |
public ReentrantLock(boolean fair) { |
sync = fair ? new FairSync() : new NonfairSync(); |
} |
5 可重入锁(递归锁)
更多推荐


所有评论(0)