Java -- マルチスレッドの同期およびロック; デッドロック (4)

Java -- マルチスレッドの同時実行性、並列性、プロセス、スレッド (1) - MinggeQingchun のブログ - CSDN ブログ

Java -- マルチスレッド終了/割り込みスレッド (2) - MinggeQingchun のブログ - CSDN ブログ - Java 割り込みスレッド

Java -- マルチスレッドの結合、生成、スリープ、スレッドの優先度、タイマー、デーモン スレッド (3) - MinggeQingchun のブログ - CSDN ブログ

Java -- マルチスレッド プロデューサー コンシューマー モード; スレッド プール ExecutorService (5) - MinggeQingchun のブログ - CSDN ブログ

一、synchronized

同期を理解する前に、スレッドセーフでない例を見てみましょう

たとえば、口座に10,000元がある場合、2人が同時にお金を引き出すと、残高が間違っているか、引き出したお金が口座の金額よりも多くなります

public class AccountThreadTest {
    public static void main(String[] args) {
        Account account = new Account("xiaoming",10000.0);

        AccountThread t1 = new AccountThread(account);
        AccountThread t2 = new AccountThread(account);

        t1.setName("t1");
        t2.setName("t2");
        t1.start();
        t2.start();
    }
}

class AccountThread extends Thread{
    private Account account;

    public AccountThread(Account account){
        this.account = account;
    }

    @Override
    public void run() {
        double money = 5000.0;

        account.withDraw(money);

        System.out.println(Thread.currentThread().getName() + "在" + account.getAccount() + "账户成功取款" + money + ";余额为" + account.getBalance());
    }
}

class Account {
    private String account;
    private Double balance;

    public Account(){

    }

    public Account(String account,Double balance){
        this.account = account;
        this.balance = balance;
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public Double getBalance() {
        return balance;
    }

    public void setBalance(Double balance) {
        this.balance = balance;
    }

    //取钱
    public void withDraw(Double money){
        Double afterMoney = this.getBalance() - money;

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        this.setBalance(afterMoney);
    }
}

出力は次のとおりです。

t2在xiaoming账户成功取款5000.0;余额为5000.0
t1在xiaoming账户成功取款5000.0;余额为5000.0

スレッド t1 と t2 は、同じアカウントからそれぞれ 5,000 を引き出し、合計金額は 10,000 ですが、残高はまだ 5,000 であり、同時マルチスレッドではデータの安全性が低下します。

(1) マルチスレッド同時環境におけるセキュリティの問題

1. マルチスレッドの同時データ セキュリティの問題

次の 3 つの条件が満たされると、スレッド セーフの問題が発生します。

1. マルチスレッド同時実行

2.共有データあり

3.共有データには変更動作があります

2. マルチスレッド同時データ セキュリティ問題の解決策: スレッド同期メカニズム

スレッド同期メカニズムは実行のためのスレッド キューイングであり、実行のためのスレッド キューイングは実行効率の一部を犠牲にしますが、データ セキュリティが第一であり、データ セキュリティは効率についてしか語ることはできず、データは安全ではなく、効率について議論することはできません。

Java 言語では、スレッドの安全性を確保するための主な手段はロックであり、Java には主に同期ロックとロックの 2 種類のロックがあります。

(二)synchronized

Synchronized は Java のキーワードです.その実装はjvm命令に基づいています.JDK1.5 より前は、並列プログラムを作成するときに例外なくスレッド同期を実現するために Synchronized を使用していましたが、JDK1 で Synchronized が実装されました.5 より前は、同期のオーバーヘッドが高かったそのため、JDK1.5以降、コードレベル(synchronizedはjvmレベル)のLockインターフェースを導入し、synchronizedと同じ機能で同期ロックを実現しました。

同期の初見

同期の公式説明は次のとおりです。

同期された同期メソッドは、スレッドの干渉とメモリの一貫性エラーを防ぐための単純な戦略の使用をサポートできます。オブジェクトが複数のスレッドから見える場合、オブジェクト変数へのすべての読み取りまたは書き込みは同期メソッドによって行われます。

簡単に言うと、Synchronized の役割は、Java で同時実行性の問題を解決するために最も一般的に使用される最も単純な方法です. これにより、最大で 1 つのスレッドが同時に同期コードを実行することが保証され、マルチスレッドでの同時実行性セキュリティの効果が保証されます。環境。コードの一部が Synchronized によって変更された場合、このコードはアトミックに実行されます。複数のスレッドがこのコードを実行している場合、それらは相互に排他的であり、互いに干渉せず、同時に実行されません。

Synchronized の動作メカニズムは、マルチスレッド環境でロックを使用することです. 最初のスレッドが実行されると、実行される前にロックを取得します. 取得されると、実行が完了するまでロックを独占するか、ロックされません.このロック、他のスレッドはロックが解除されるまでブロックして待機することしかできません

Synchronized は、Java でネイティブにサポートされている Java のキーワードであり、最も基本的な同期ロックです。

同期によって変更されるオブジェクトは次のとおりです。 

1. コードブロックの修正 修正されたコードブロックを同期文ブロックと呼び、その動作範囲は{}で囲まれたコードであり、動作対象はこのコードブロックを呼び出すオブジェクトです。

2. メソッドの修正 修正されたメソッドを同期メソッドと呼び、その動作範囲はメソッド全体であり、動作対象はこのメソッドを呼び出すオブジェクトです。

3.静的メソッドを変更するには、その効果の範囲は静的メソッド全体であり、アクションのオブジェクトはこのクラスのすべてのオブジェクトです

4. クラスを変更する場合、その効果の範囲は同期後の括弧内の部分であり、主な動作対象はこのクラスのすべてのオブジェクトです。

1.共通メソッドの修正

/**
 * synchronized 修饰普通方法
 */
public synchronized void method() {
    // ....
}

同期が通常のメソッドを変更する場合、変更されたメソッドは同期メソッドと呼ばれ、その動作範囲はメソッド全体であり、動作の対象はこのメソッドを呼び出すオブジェクトです。

2.静的メソッドの修正

/**
 * synchronized 修饰静态方法
 */
public static synchronized void staticMethod() {
    // .......
}

同期が静的メソッドを変更する場合、そのアクションの範囲はプログラム全体であり、このロックは、このロックを呼び出すすべてのオブジェクトに対して相互に排他的です 

静的メソッドの場合、同期ロックはグローバルです。つまり、プログラム全体の実行中、この静的メソッドを呼び出すすべてのオブジェクトは相互に排他的ですが、通常のメソッドはオブジェクト レベルを対象としており、さまざまなオブジェクトがさまざまなロックに対応します。

静的メソッド ロックはグローバルであり、すべての呼び出し元を対象としています。共通メソッド ロックはオブジェクト レベルであり、異なるオブジェクトには異なるロックがあります。

オブジェクト ロック: 1 オブジェクトに対して 1 ロック、100 オブジェクトに対して 100 ロック
クラス ロック: 100 オブジェクト、または 1 クラス ロック

3.コードブロックを修正する

日常の開発では、メソッドをロックすることはメソッド全体をロックすることと同じであるため、メソッドの代わりにコード ブロックをロックすることが最も一般的に使用されます.この場合、ロックの粒度が大きすぎて、プログラムの実行が遅くなります.パフォーマンスが影響を受けるため、通常は synchronized を使用してコード ブロックをロックします。その実装構文は次のとおりです。

public void classMethod() throws InterruptedException {
    // 前置代码...
    
    // 加锁代码
    synchronized (SynchronizedUsage.class) {
        // ......
    }
    
    // 后置代码...
}

変更されたメソッドと比較して、変更されたコード ブロックはロックされたオブジェクトを手動で指定する必要があり、ロックされたオブジェクトは通常、this または xxx.class の形式で表現されます。

// 加锁某个类
synchronized (SynchronizedUsage.class) {
    // ......
}

// 加锁当前类对象
synchronized (this) {
    // ......
}

このVSクラス

synchronized を使用してこれをロックするのと xxx.class はまったく異なります. これをロックする場合は、現在のオブジェクトがロックに使用され、各オブジェクトがロックに対応することを意味します. xxx.class を使用してロックする場合は、クラスを使用することを意味します (これはアプリケーション レベルであり、グローバルに有効です。

次の 4 つの例から区別できます。

/**
 doOther方法执行的时候需要等待doSome方法的结束吗?
 不需要,因为doOther()方法没有synchronized
 */
public class SynchronizedExam {
    public static void main(String[] args) {
        MyClass myClass = new MyClass();

        MyThread t1 = new MyThread(myClass);
        t1.setName("t1");

        MyThread t2 = new MyThread(myClass);
        t2.setName("t2");

        t1.start();
        // 睡眠的作用:保证t1线程先执行
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();
    }
}

class MyThread extends Thread{
    private MyClass myClass;

    public MyThread(){
    }

    public MyThread(MyClass myClass){
        this.myClass = myClass;
    }

    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("t1")){
            myClass.doSome();
        }
        if (Thread.currentThread().getName().equals("t2")){
            myClass.doOther();
        }
    }
}

class MyClass {
    public synchronized void doSome(){
        System.out.println("doSome begin");
        try {
            Thread.sleep(1000 * 5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }

    public void doOther(){
        System.out.println("doOther begin");
        System.out.println("doOther over");
    }
}

出力

doSome begin
doOther begin
doOther over
doSome over
/**
 doOther方法执行的时候需要等待doSome方法的结束吗?
 需要,doOther()方法有synchronized
 */
public class SynchronizedExam {
    public static void main(String[] args) {
        MyClass myClass = new MyClass();

        MyThread t1 = new MyThread(myClass);
        t1.setName("t1");

        MyThread t2 = new MyThread(myClass);
        t2.setName("t2");

        t1.start();
        // 睡眠的作用:保证t1线程先执行
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();
    }
}

class MyThread extends Thread{
    private MyClass myClass;

    public MyThread(){
    }

    public MyThread(MyClass myClass){
        this.myClass = myClass;
    }

    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("t1")){
            myClass.doSome();
        }
        if (Thread.currentThread().getName().equals("t2")){
            myClass.doOther();
        }
    }
}

class MyClass {
    // 普通的同步方法 锁的调用者,如 this对象,obj对象; Object obj = new Object();
    public synchronized void doSome(){
        System.out.println("doSome begin");
        try {
            Thread.sleep(1000 * 5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }

    public synchronized void doOther(){
        System.out.println("doOther begin");
        System.out.println("doOther over");
    }
}

出力

doSome begin
doSome over
doOther begin
doOther over
/**
 doOther方法执行的时候需要等待doSome方法的结束吗?
 不需要,因为MyClass对象是两个,两把锁
 */
public class SynchronizedExam {
    public static void main(String[] args) {
        MyClass myClass1 = new MyClass();
        MyClass myClass2 = new MyClass();

        MyThread t1 = new MyThread(myClass1);
        t1.setName("t1");

        MyThread t2 = new MyThread(myClass2);
        t2.setName("t2");

        t1.start();
        // 睡眠的作用:保证t1线程先执行
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();
    }
}

class MyThread extends Thread{
    private MyClass myClass;

    public MyThread(){
    }

    public MyThread(MyClass myClass){
        this.myClass = myClass;
    }

    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("t1")){
            myClass.doSome();
        }
        if (Thread.currentThread().getName().equals("t2")){
            myClass.doOther();
        }
    }
}

class MyClass {
    // 普通的同步方法 锁的调用者,如 this对象,obj对象; Object obj = new Object();
    public synchronized void doSome(){
        System.out.println("doSome begin");
        try {
            Thread.sleep(1000 * 5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }

    public synchronized void doOther(){
        System.out.println("doOther begin");
        System.out.println("doOther over");
    }
}

出力

doSome begin
doOther begin
doOther over
doSome over
/**
 doOther方法执行的时候需要等待doSome方法的结束吗?
 需要,因为静态方法是类锁,不管创建了几个对象,类锁只有1把
 */
public class SynchronizedExam {
    public static void main(String[] args) {
        MyClass myClass1 = new MyClass();
        MyClass myClass2 = new MyClass();

        MyThread t1 = new MyThread(myClass1);
        t1.setName("t1");

        MyThread t2 = new MyThread(myClass2);
        t2.setName("t2");

        t1.start();
        // 睡眠的作用:保证t1线程先执行
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();
    }
}

class MyThread extends Thread{
    private MyClass myClass;

    public MyThread(){
    }

    public MyThread(MyClass myClass){
        this.myClass = myClass;
    }

    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("t1")){
            myClass.doSome();
        }
        if (Thread.currentThread().getName().equals("t2")){
            myClass.doOther();
        }
    }
}

class MyClass {
    // 静态的同步方法 锁的是 Class 类模板(.class文件;一个类只有唯一的一份.class文件)
    // synchronized 锁的对象是方法的调用者!
    // static 静态方法
    // 类一加载就有了!锁的是Class
    public synchronized static void doSome(){
        System.out.println("doSome begin");
        try {
            Thread.sleep(1000 * 5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }

    public synchronized static void doOther(){
        System.out.println("doOther begin");
        System.out.println("doOther over");
    }
}

出力

doSome begin
doSome over
doOther begin
doOther over

上記の出金の例では、アカウントの共有オブジェクトにロックを追加するだけで済みます

class Account {
    private String account;
    private Double balance;

    // 实例变量Account对象是多线程共享的,Account对象中的实例变量obj也是共享的)
    Object obj = new Object();

    public Account(){

    }

    public Account(String account,Double balance){
        this.account = account;
        this.balance = balance;
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public Double getBalance() {
        return balance;
    }

    public void setBalance(Double balance) {
        this.balance = balance;
    }

    /**
     线程同步机制的语法是:
     synchronized(){
        // 线程同步代码块
     }
     被synchronized修饰的代码块及方法,在同一时间,只能被单个线程访问
     synchronized()小括号中传的“数据”必须是多线程共享的数据,才能达到多线程排队

     ()中参数
     假设t1、t2、t3、t4、t5,有5个线程
     希望t1 t2 t3排队,t4 t5不需要排队
     在()中写一个t1 t2 t3共享的对象,而这个对象对于t4 t5来说不是共享的

     synchronized()执行原理
     1、假设t1和t2线程并发,开始执行代码时,有一个先后顺序
     2、假设t1先执行,遇到了synchronized,t1自动找“共享对象”的对象锁,
     找到之后并占有这把锁,然后执行同步代码块中的程序,在程序执行过程中一直
     占有这把锁的,直到同步代码块代码结束,这把锁才会释放
     3、假设t1已经占有这把锁,此时t2也遇到synchronized关键字,也会去占有
     共享对象的这把锁,结果这把锁被t1占有,t2只能在同步代码块外面等待t1的结束,
     直到t1把同步代码块执行结束了,t1会归还这把锁,此时t2终于等到这把锁,然后
     t2占有这把锁之后,进入同步代码块执行程序
     */

    //取钱
    public void withDraw(Double money){
//        Object obj2 = new Object();

//        synchronized (obj) {
        //synchronized ("abc") { // "abc"在字符串常量池当中,会被所有线程共享
        //synchronized (null) { // 报错:空指针
//        synchronized (obj2) { // obj2是局部变量,不是共享对象
        synchronized (this){
            Double afterMoney = this.getBalance() - money;

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            this.setBalance(afterMoney);
        }
    }
}

(3) Synchronized() 実行原理

1. t1 スレッドと t2 スレッドが並行していると仮定すると、コードの実行開始時にシーケンスが存在します。

2. t1 が最初に実行されると仮定すると、synchronized に遭遇すると、t1 は自動的に「共有オブジェクト」のオブジェクト ロックを見つけて、それを見つけてロックを保持し、次に同期コード ブロック内のプログラムを実行し、すべてのロックを保持します。プログラム実行プロセス中の時間. このロックは、同期されたコード ブロック コードが終了するまで解放されません。

3. t1 が既にこのロックを所有していると仮定すると、t2 もこの時点で同期キーワードに遭遇し、共有オブジェクトのこのロックも占有します.結果として、このロックは t1 によって占有され、t2 は外で t1 を待つことしかできません.同期コードブロックの終了、t1 が同期コードブロックの実行を終了するまで、t1 はロックを返します。この時点で、t2 は最終的にロックを待機し、t2 はロックを取得して、同期コードブロック実行プログラムに入ります。

2、ロック

JDK 5.0 以降、Java はより強力なスレッド同期メカニズムを提供します。同期は、同期ロック オブジェクトを明示的に定義することによって実現されます。同期ロックは Lock オブジェクトを次のように使用します。

java.util.concurrent.locks.Lockインターフェイスは、複数のスレッドによる共有リソースへのアクセスを制御するためのツールです。

ロックは、共有リソースへの排他的アクセスを提供します。一度に 1 つのスレッドだけが Lock オブジェクトをロックできます。スレッドが共有リソースへのアクセスを開始する前に、最初に Lock オブジェクトを取得する必要があります。

The ReentrantLock class implements Lock, which has the same concurrency and memory semantics as synchronized. スレッドセーフな制御の実装では、ReentrantLock がより一般的に使用され、ロックを明示的にロックおよび解放できます。

以下のように、チケット購入プロセスをシミュレートします

public class LockTest {
    public static void main(String[] args) {
        BuyTicketThread buyTicketThread = new BuyTicketThread();

        Thread t1 = new Thread(buyTicketThread);
        Thread t2 = new Thread(buyTicketThread);
        Thread t3 = new Thread(buyTicketThread);

        t1.setName("小明");
        t2.setName("小李");
        t3.setName("小张");

        t1.start();
        t2.start();
        t3.start();
    }
}

class BuyTicketThread implements Runnable{
    private int ticketNum = 10;
    private boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            buy();
        }
    }

    //synchronized 同步方法,锁的是this
    private /*synchronized*/ void buy() {
        if (ticketNum <= 0){
            flag = false;
            return;
        }

        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "购买了第" + ticketNum-- + "张票");
    }
}

出力

小李购买了第9张票
小张购买了第10张票
小明购买了第8张票
小张购买了第5张票
小明购买了第6张票
小李购买了第7张票
小张购买了第4张票
小明购买了第2张票
小李购买了第3张票
小张购买了第0张票
小明购买了第-1张票
小李购买了第1张票

ロックされていない場合、複数の異なるスレッドが同じチケット、または 0 と負の数を購入する原因となります; チケット購入方法に同期変更を追加すると、チケット購入が正常であることを確認できます

import java.util.concurrent.locks.ReentrantLock;

public class LockTest {
    public static void main(String[] args) {
        BuyTicketThread buyTicketThread = new BuyTicketThread();

        Thread t1 = new Thread(buyTicketThread);
        Thread t2 = new Thread(buyTicketThread);
        Thread t3 = new Thread(buyTicketThread);

        t1.setName("小明");
        t2.setName("小李");
        t3.setName("小张");

        t1.start();
        t2.start();
        t3.start();
    }
}

class BuyTicketThread implements Runnable{
    private int ticketNum = 10;

    //定义lock锁
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                //加锁
                lock.lock();
                if (ticketNum > 0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "购买了第" + ticketNum-- + "张票");
                }
            }finally {
                //解锁
                lock.unlock();
            }
        }
    }
}

ロックは Lock オブジェクトをロックします。そのロック メソッドが呼び出されると、Lock クラスのフラグ状態が 1 増加します (状態は実際には、Lock の内部クラスの親クラスである AbstractQueuedSynchronizer クラスの変数です)。 、ロックが解除されると、状態が 1 減少します (1 を追加して 1 を減少させる操作は、再入可能にするためです)。

ノート:

ロックは明示的なロックであり、手動でロックを閉じる必要があります (手動でロックを開閉します。ロックを閉じるのを忘れるとデッドロックが発生します) 

ロックは手動で閉じて解放する必要があり、finally 句で解放する必要があります

ReentrantLock クラスのソース コード コメント

3.同期とロックの違い

1. Lock はインターフェースであり、synchronized は Java の組み込みキーワードです

2. Lock はロックが取得されたかどうかを判断できますが、synchronized はロックの状態を判断できません。

3. Lock は明示的なロックであり、ロックは手動で閉じる必要があります (手動でロックを開閉すると、ロックを閉じるのを忘れるとデッドロックます範囲外

4. Lock には多数の同期コード ブロックに適したコード ブロック ロックのみがあり、Synchroned には少量のコードに適したコード ブロック ロックとメソッド ロックがあります。

5. Lock ロックを使用すると、JVM がスレッドのスケジューリングに費やす時間が短縮され、パフォーマンスが向上します。これにより、必ずしもスレッドがキューに入れられてブロックを待つ必要がなくなります。また、より優れたスケーラビリティ (より多くのサブクラスを提供) を備えています; 同期すると、スレッドがキューに入れられ、ブロックを待機します

6. ロック、再入可能ロック、ロック状態を判断できる、不公平; 同期再入可能ロック、中断不可、不公平

優先使用

ロック > 同期コード ブロック (メソッド本体に入り、対応するリソースが割り当てられている) > 同期メソッド (メソッド本体の外側)

第 4 に、Java の 3 つの主要な変数のスレッド セーフの問題

1. Java の 3 つの変数

1. インスタンス変数: ヒープ内

2. 静的変数: メソッド領域

3. ローカル変数: スタック上

ローカル変数は共有されない (スレッドごとに 1 つのスタック) ため、ローカル変数は決してスレッドセーフにはなりません。ローカル変数はスタックにあります。したがって、ローカル変数は決して共有されません

 インスタンス変数はヒープ内にあり、ヒープは 1 つだけです

 静的変数はメソッド領域にあり、メソッド領域は 1 つだけです

ヒープとメソッド領域の両方が複数のスレッドで共有されるため、スレッド セーフの問題が発生する可能性があります

ローカル変数 + 定数: スレッド セーフの問題なし

メンバー変数: スレッド セーフの問題がある可能性があります

2. ローカル変数を使用する

推奨される用途: StringBuilder (スレッドセーフでない)

ローカル変数にはスレッド セーフの問題がないため

StringBuilder を選択してください; StringBuffer (スレッドセーフ、ソース コード内のメソッドは同期で変更されます) は比較的非効率的です

ArrayList はスレッドセーフではありません

ベクトルはスレッドセーフです

HashMap HashSet はスレッドセーフではありません

Hashtable はスレッドセーフです。

3. 開発中にスレッドの安全性の問題を解決する

開発の最初にスレッド同期同期を選択しません; 同期はプログラムの実行効率を低下させ、ユーザー エクスペリエンスは良くありません。システムのユーザー スループットが低下します。ユーザーエクスペリエンスが悪い。最後の手段としてスレッド同期メカニズムを選択する

最初の解決策: 「インスタンス変数と静的変数」の代わりにローカル変数を使用してみてください

2 番目の解決策: インスタンス変数でなければならない場合は、インスタンス変数のメモリが共有されないように、複数のオブジェクトを作成することを検討できます (1 つのスレッドが 1 つのオブジェクトに対応し、100 のスレッドが 100 のオブジェクトに対応し、オブジェクトは共有されません)。 、データセキュリティの問題はありません)

3番目の解決策: ローカル変数が使用できず、複数のオブジェクトを作成できない場合は、この時点で Synchronized (スレッド同期メカニズム) を選択することしかできません。

五、デッドロック

スレッド 1 はリソース B を保持し、スレッド 2 はリソース A を保持します。両者は同時に互いのリソースを申請しようとするため、これら 2 つのスレッドは相互に待機し、デッドロック状態になります。

デッドロック現象でエラー報告や異常が発生しない プログラムがそこでデッドロックしており、発見・デバッグが困難

1.デッドロックの必要条件

デッドロックが発生するには、次の 4 つの条件が満たされている必要があります。

1. 相互排除条件: リソースは、常に 1 つのスレッドによってのみ占有されます。

2. 要求と保留の条件: リソースを要求したためにプロセスがブロックされた場合、プロセスは取得したリソースを解放しません。

3.非剥奪条件:スレッドが獲得した資源は、使い切る前に他のスレッドに強制的に剥奪することができず、使い切った後にのみ解放される

4. 循環待機状態: 多数のプロセスが頭と尾の循環待機リソース関係を形成します。

2.スレッドのデッドロックを回避する

デッドロックの必要条件は上記の4つですが、デッドロックを回避するためには、デッドロックの4つの条件のうちの1つを破壊するだけで済みます。

1. 相互排他条件を破棄する: この条件を破棄する方法はありません。これは、ロックを使用してそれらを相互に排他的にするためです (重要なリソースには相互排他アクセスが必要です)。

2. 破壊依頼・整備条件:全資源一括申請

3. 破壊および非剥奪条件: 一部のリソースを占有しているスレッドがさらに他のリソースを申請する場合、アプリケーションを取得できない場合、占有しているリソースを積極的に解放できます。

4. 循環待ち条件の破棄: リソースを順番に申請することで防止します。リソースを特定の順序で申請し、逆の順序でリソースをリリースします。ブレークループ待ち状態

//死锁:
public class DeadLock {
    public static void main(String[] args) {
        Object obj1 = new Object();
        Object obj2 = new Object();

        MyThread1 t1 = new MyThread1(obj1,obj2);
        MyThread2 t2 = new MyThread2(obj1,obj2);
        t1.setName("t1");
        t2.setName("t2");
        t1.start();
        t2.start();
    }
}

class MyThread1 extends Thread{
    private Object obj1;
    private Object obj2;

    public MyThread1(){
    }

    public MyThread1(Object obj1,Object obj2){
        this.obj1 = obj1;
        this.obj2 = obj2;
    }

    @Override
    public void run() {
        synchronized (obj1){

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (obj2){

            }
        }
    }
}

class MyThread2 extends Thread{
    private Object obj1;
    private Object obj2;

    public MyThread2(){
    }

    public MyThread2(Object obj1,Object obj2){
        this.obj1 = obj1;
        this.obj2 = obj2;
    }

    @Override
    public void run() {
        synchronized (obj2){

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (obj1){

            }
        }
    }
}

参照リンク

https://www.jb51.net/article/244365.htm

おすすめ

転載: blog.csdn.net/MinggeQingchun/article/details/127272913