死鎖:
1)不同的執行緒分別占用對方需要的同步資源不放棄,都在等待對方放棄自己需要的同步資源,就形成了執行緒的死鎖,
2)出現死鎖后,不會出現例外,不會出現提示,只是所有的執行緒都出于阻塞狀態,無法繼續,
產生死鎖的四個必要條件:
1)互斥條件:一個資源每次只能被一個行程使用,
2)請求與保持條件:一個行程因請求資源而阻塞時,對已獲得的資源保持不放,
3)不剝奪條件:行程已獲得的資源,在末使用完之前,不能強行剝奪,
4)回圈等待條件:若干行程之間形成一種頭尾相接的回圈等待資源關系,
處理死鎖的四種方法:
1)預防死鎖
2)避免死鎖
3)檢測死鎖
4)解除死鎖
解決方法:
1)專門的演算法、原則
2)盡量減少同步資源的定義
3)盡量避免嵌套同步
我們使用的同時要避免出現死鎖:
//死鎖演示
class A{
public synchronized void foo(B b){//同步監視器:A類的物件:a
System.out.println("當前執行緒名:"+Thread.currentThread().getName()+"進入了A實體的foo方法");//1
try{
Thread.sleep(200);
}catch(InterruptedException ex){
ex.printStackTrace();
}
System.out.println("當前執行緒名:"+Thread.currentThread().getName()+"企圖呼叫A實體的last方法");//3
b.last();
}
public synchronized void last(){System.out.println("進入了A類的last方法內部");}
}
class B{
public synchronized void bar(A a){//同步監視器:b
System.out.println("當前執行緒名:"+Thread.currentThread().getName()+"進入了B實體的bar方法");//2
try{
Thread.sleep(200);
}catch (InterruptedException ex){
ex.printStackTrace();
}
System.out.println("當前執行緒名:"+Thread.currentThread().getName()+"企圖呼叫A實體的last方法");//4
a.last();
}
public synchronized void last(){System.out.println("進入了人B類的last方法內部");}
}
public class DeadLock implements Runnable{
A a=new A();
B b=new B();
public void init(){
Thread.currentThread().setName("主執行緒");
//呼叫a物件的foo方法
a.foo(b);
System.out.println("進入了主執行緒之后");
}
public void run(){
Thread.currentThread().setName("副執行緒");
//呼叫b物件的bar方法
b.bar(a);
System.out.println("進入了副執行緒之后");
}
public static void main(String[] args) {
DeadLock d1=new DeadLock();
new Thread(d1).start();
d1.init();
}
}
LOCK(鎖):
1)從JDK 5.0開始,Java提供了更加強大的執行緒同步機制----通過顯式定義同步鎖物件來實作同步,同步鎖使用Lock物件來充當,
2)java.util.concurrent.locks.Lock介面是控制多個執行緒對共享資源進行訪問的工具,鎖提供了對共享資源的獨占訪問,每次只能有一個執行緒對Lock物件加鎖,執行緒開始訪問共享資源之前應先獲得Lock物件,
3)ReentrantLock類實作了Lock,它擁有synchronized相同的并發性和記憶體語意,在實作執行緒安全的控制中,比較常用的是ReentrantLock,可以顯式加鎖、釋放鎖,
解決執行緒安全問題的方式三:Lock鎖—JDK5.0新增
面試題:synchronized與Lock的異同?
相同:二者都可以解決執行緒安全問題
不同:synchronized機制在執行完相應的同步代碼以后,自動的釋放同步監視器
Lock需要手動啟動同步(Lock()),同時結束同步也需要手動得實作(unlock())
synchronized與Lock的對比:
1)Lock是顯式鎖(手動開啟和關閉所,別忘記關閉鎖),synchronized是隱形鎖,出了作用域自動釋放,
2)Lock只有代碼塊鎖,synchronized有代碼塊鎖和方法鎖,
3)使用Lock鎖,JVM將花費較少的時間來調度執行緒,性能更好,并且具有更好的擴展性(提供更多的子類),
優先使用順序:
Lock-->同步代碼塊(已經進入了方法體,分配了相應資源)-->同步方法(在方法體之外)
import java.util.concurrent.locks.ReentrantLock;
class Window implements Runnable{
private int ticket=100;
//1.實體化ReentrantLock
private ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
while (true){
try{
//2.呼叫鎖定方法Lock()
lock.lock();
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+":售票,票號為"+ticket);
ticket--;
}else{
break;
}
}finally {
//3.呼叫解鎖方法:unlock()
lock.unlock();
}
}
}
}
public class LockTest {
public static void main(String[] args) {
Window w=new Window();
Thread t1=new Thread(w);
Thread t2=new Thread(w);
Thread t3=new Thread(w);
t1.setName("視窗1");
t2.setName("視窗2");
t3.setName("視窗3");
t1.start();
t2.start();
t3.start();
}
}
/*
銀行有一個賬戶,
有兩個儲戶分別向同一個賬戶存3000元,每次存1000,存3次,每次存完列印賬戶余額
分析:
1.是否是多執行緒問題?是兩個出乎執行緒
2.是否有共享資料?有,賬戶(或賬戶余額)
3.是否有執行緒安全問題?有
4.需要考慮如何解決執行緒安全問題?同步機制:有三種方式
*/
class Account{
private double balance;
public Account(double balance) {
this.balance = balance;
}
//存錢
//一個存完后另一個才存
public synchronizes void deposit(double amt){
public void deposit(double amt){
if(amt>0){
balance+=amt;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("存錢成功,余額為:"+balance);
}
}
}
class Customer extends Thread{
private Account acct;
public Customer(Account acct){
this.acct=acct;
}
@Override
public void run() {
for(int i=0;i<3;i++){
acct.deposit(1000);
}
}
}
public class AccountTest {
public static void main(String[] args) {
Account acct=new Account(0);
Customer c1=new Customer(acct);
Customer c2=new Customer(acct);
c1.setName("甲");
c2.setName("乙");
c1.start();
c2.start();
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/297076.html
標籤:其他
上一篇:基于 Python 環境搭建 - YOLO 實作吸煙行為監測
下一篇:暴力破解繞過token限制
