常見鎖介紹 synchronized鎖的八中情況 package com.shaonian.juc.more_thread_lock; import java.util.concurrent.TimeUnit; class Phone { public static synchronized voi ...
常見鎖介紹
synchronized鎖的八中情況
package com.shaonian.juc.more_thread_lock;
import java.util.concurrent.TimeUnit;
class Phone {
public static synchronized void sendSMS() throws Exception {
//停留4秒
TimeUnit.SECONDS.sleep(4);
System.out.println("------sendSMS");
}
public synchronized void sendEmail() throws Exception {
System.out.println("------sendEmail");
}
public void getHello() {
System.out.println("------getHello");
}
}
/**
* @Description: 8鎖
*
1 標準訪問,先列印簡訊還是郵件
------sendSMS
------sendEmail
2 停4秒在簡訊方法內,先列印簡訊還是郵件
------sendSMS
------sendEmail
3 新增普通的hello方法,是先打簡訊還是hello
------getHello
------sendSMS
4 現在有兩部手機,先列印簡訊還是郵件
------sendEmail
------sendSMS
5 兩個靜態同步方法,1部手機,先列印簡訊還是郵件
------sendSMS
------sendEmail
6 兩個靜態同步方法,2部手機,先列印簡訊還是郵件
------sendSMS
------sendEmail
7 1個靜態同步方法,1個普通同步方法,1部手機,先列印簡訊還是郵件
------sendEmail
------sendSMS
8 1個靜態同步方法,1個普通同步方法,2部手機,先列印簡訊還是郵件
------sendEmail
------sendSMS
*/
public class Lock8QuestionBySynchronized {
public static void main(String[] args) throws Exception {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
try {
phone.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, "AA").start();
Thread.sleep(100);
new Thread(() -> {
try {
// phone.sendEmail();
// phone.getHello();
phone2.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "BB").start();
}
}
代碼結果原因分析,見博客中synchronized關鍵字回顧。
ReentrantLock讀寫鎖分析
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();//預設是非公平鎖
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {//傳入false,定義的就是非公平的讀寫鎖
sync = fair ? new FairSync() : new NonfairSync();
}
非公平鎖(ReentrantLock實現)
非公平鎖是多個線程加鎖時直接嘗試獲取鎖,能搶到鎖到直接占有鎖,搶不到才會到等待隊列的隊尾等待。確定可能會出現線程餓死現象(鎖一直被一個線程持有,某些線程一直拿不到鎖),優點是執行效率高,可以直接獲取鎖。
static final class NonfairSync extends Sync {//ReentrantLock中的靜態內部類
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
公平鎖(ReentrantLock實現)
公平鎖是指多個線程按照申請鎖的順序來獲取鎖,線程直接進入隊列中排隊,隊列中的第一個線程才能獲得鎖。保證每個線程都能拿到鎖,優點是執行效率比非公平鎖低,優點是不會出現線程餓死現象。
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
可重入鎖(ReentrantLock實現)
synchronized(隱式)和Lock(顯式)都是可重入鎖,也叫遞歸鎖。特點,類似於,有了第一把鎖,此鎖內部鎖的代碼都可以訪問。像開了大門,開剩下的門就不用再用鑰匙開門了。
/**
* @author 長名06
* @version 1.0
* 可重入鎖案例
*/
public class ReentrantLockDemo {
private synchronized void add(){
add();
}
public static void main(String[] args) {
//new ReentrantLockDemo().add();//會出現StackOverflowError
Object o = new Object();
new Thread(() -> {
synchronized (o){
System.out.println(Thread.currentThread().getName() + "外部");
synchronized (o){
System.out.println(Thread.currentThread().getName() + "中部");
synchronized (o){
System.out.println(Thread.currentThread().getName() + "內部");
}
}
}
},"aa").start();
ReentrantLock lock = new ReentrantLock();
new Thread(() -> {
try{
lock.lock();
System.out.println(Thread.currentThread().getName() + "外層操作");
try{
lock.lock();
System.out.println(Thread.currentThread().getName() + "內層操作");
}finally {
lock.unlock();
}
}finally {
lock.unlock();
}
}).start();
//結果
//Thread-0外層操作
//Thread-0內層操作
}
}
死鎖
基本介紹
死鎖,是一種多個線程在執行過程中,因競爭資源時,導致線程互相持有其他線程執行需要的鎖,從而引起線程等待的現象。在沒有外力的作用下,程式會崩潰。
引起死鎖的原因
- 1.系統資源不足。
- 2.進程運行順序不合適。
- 3.資源分配不當。
/**
* @author 長名06
* @version 1.0
* 死鎖代碼
*/
public class DeadLock {
static Object a = new Object();
static Object b = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (a){
System.out.println(Thread.currentThread().getName() + "持有鎖a,試圖後去鎖b");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (b){
System.out.println(Thread.currentThread().getName() + "獲取鎖b");
}
}
},"a").start();
new Thread(() -> {
synchronized (b){
System.out.println(Thread.currentThread().getName() + "持有鎖b,試圖後去鎖a");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (a){
System.out.println(Thread.currentThread().getName() + "獲取鎖a");
}
}
},"b").start();
}
}
驗證線程一直等待是否是死鎖
- 1.jps -l 命令類似linux下的ps - ef 可以查詢java的線程
正在執行的死鎖代碼的進程id - 2.jstack工具(JVM 自帶的堆棧工具) jstack 線程id 可以查詢對應線程的信息
執行jstack pid指令
提示語句中會有 Found 1 deadlock
只是為了記錄自己的學習歷程,且本人水平有限,不對之處,請指正。