首页 > 编程语言 >Java面试题:顺序锁和轮询锁解决死锁问题

Java面试题:顺序锁和轮询锁解决死锁问题

时间:2023-08-28 10:00:49浏览次数:41  
标签:面试题 Java Thread System 获取 死锁 线程 println out

(目录)

死锁(Dead Lock)示例

两个线程

  • 线程1:先获取锁A,再获取锁B
  • 线程2:先获取锁B,再获取锁A
package com.example.demo;

public class DeadLockExample {
    public static void main(String[] args) {
        Object lockA = new Object(); // 创建锁 A
        Object lockB = new Object(); // 创建锁 B

        // 创建线程 1
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lockA) {
                    System.out.println("线程 1:获取到锁 A!");

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    
                    System.out.println("线程 1:等待获取 B...");

                    synchronized (lockB) {
                        System.out.println("线程 1:获取到锁 B!");
                    }
                }
            }
        });
        t1.start(); // 运行线程

        // 创建线程 2
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lockB) {
                    System.out.println("线程 2:获取到锁 B!");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    
                    System.out.println("线程 2:等待获取 A...");

                    synchronized (lockA) {
                        System.out.println("线程 2:获取到锁 A!");
                    }
                }
            }
        });
        t2.start(); // 运行线程
    }
}

程序会一直等待

线程 1:获取到锁 A!
线程 2:获取到锁 B!
线程 1:等待获取 B...
线程 2:等待获取 A...

解决方案1:顺序锁

两个线程

  • 线程1:先获取锁A,再获取锁B
  • 线程2:先获取锁A,再获取锁B
package com.example.demo;

public class SolveDeadLockExample {
    public static void main(String[] args) {
        Object lockA = new Object(); // 创建锁 A
        Object lockB = new Object(); // 创建锁 B

        // 创建线程 1
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lockA) {
                    System.out.println("线程 1:获取到锁 A!");

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

                    System.out.println("线程 1:等待获取 B...");

                    synchronized (lockB) {
                        System.out.println("线程 1:获取到锁 B!");
                    }
                }
            }
        });

        t1.start(); // 运行线程

        // 创建线程 2
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lockA) {
                    System.out.println("线程 2:获取到锁 A!");

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

                    System.out.println("线程 2:等待获取B...");

                    synchronized (lockB) {
                        System.out.println("线程 2:获取到锁 B!");
                    }
                }
            }
        });
        t2.start(); // 运行线程
    }
}

程序执行完成

线程 1:获取到锁 A!
线程 1:等待获取 B...
线程 1:获取到锁 B!
线程 2:获取到锁 A!
线程 2:等待获取B...
线程 2:获取到锁 B!

解决方案2:轮询锁

两个线程

  • 线程1:先获取锁A,再获取锁B,如果失败,则释放所有锁
  • 线程2:先获取锁B,再获取锁A
package com.example.demo;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SolveDeadLockExample {

    public static void main(String[] args) {
        Lock lockA = new ReentrantLock(); // 创建锁 A
        Lock lockB = new ReentrantLock(); // 创建锁 B

        // 创建线程 1(使用轮询锁)
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                // 调用轮询锁
                pollingLock(lockA, lockB);
            }
        });
        t1.start(); // 运行线程

        // 创建线程 2
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                lockB.lock(); // 加锁
                System.out.println("线程 2:获取到锁 B!");
                try {
                    Thread.sleep(1000);
                    System.out.println("线程 2:等待获取 A...");
                    lockA.lock(); // 加锁
                    try {
                        System.out.println("线程 2:获取到锁 A!");
                    } finally {
                        lockA.unlock(); // 释放锁
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lockB.unlock(); // 释放锁
                }
            }
        });
        t2.start(); // 运行线程
    }

    /**
     * 轮询锁
     */
    public static void pollingLock(Lock lockA, Lock lockB) {
        while (true) {
            if (lockA.tryLock()) { // 尝试获取锁
                System.out.println("线程 1:获取到锁 A!");
                try {
                    Thread.sleep(1000);
                    System.out.println("线程 1:等待获取 B...");
                    if (lockB.tryLock()) { // 尝试获取锁
                        try {
                            System.out.println("线程 1:获取到锁 B!");
                        } finally {
                            lockB.unlock(); // 释放锁
                            System.out.println("线程 1:释放锁 B.");
                            break;
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lockA.unlock(); // 释放锁
                    System.out.println("线程 1:释放锁 A.");
                }
            }

            // 等待一秒再继续执行
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

程序执行完成

线程 1:获取到锁 A!
线程 2:获取到锁 B!
线程 1:等待获取 B...
线程 2:等待获取 A...
线程 1:释放锁 A.
线程 2:获取到锁 A!
线程 1:获取到锁 A!
线程 1:等待获取 B...
线程 1:获取到锁 B!
线程 1:释放锁 B.
线程 1:释放锁 A.

参考文章

标签:面试题,Java,Thread,System,获取,死锁,线程,println,out
From: https://blog.51cto.com/mouday/7260377

相关文章

  • 20230628 java.sql.DriverManager
    介绍java.sql.DriverManagerpublicclassDriverManager驱动管理器根据API编写的程序都可以与驱动管理器进行通信,而驱动管理器则通过驱动程序与实际的数据库进行通信APIstaticgetConnectionConnectiongetLogWriter,setLogWritergetLoginTimeout,setLoginT......
  • 20230628 java.sql.ResultSet
    介绍java.sql.ResultSetpublicinterfaceResultSetextendsWrapper,AutoCloseable结果集结果集支持滚动,支持更新,默认不开启API常量FetchDirectionFETCH_FORWARD:1000FETCH_REVERSE:1001FETCH_UNKNOWN:1002resultSetTypeTYPE_FORWARD_ONLY:1003结果......
  • 20230628 java.sql.SQLException
    介绍java.sql.SQLExceptionpublicclassSQLExceptionextendsjava.lang.ExceptionimplementsIterable每个SQLException都有一个由多个SQLException对象构成的链有大量的异常子类,按照树结构组织API构造器SQLException()SQLException(Stringreason)SQLExceptio......
  • 20230628 java.sql.Statement
    介绍java.sql.StatementpublicinterfaceStatementextendsWrapper,AutoCloseable语句API常量SUCCESS_NO_INFO:-2EXECUTE_FAILED:-3getMoreResultsCLOSE_CURRENT_RESULT:1KEEP_CURRENT_RESULT:2CLOSE_ALL_RESULTS:3autoGeneratedKeysRETURN_GENE......
  • 20230629 java.sql.CallableStatement
    介绍java.sql.CallableStatementpublicinterfaceCallableStatementextendsPreparedStatementAPIpublicregisterOutParameterwasNullset/getgetArraygetRefsetAsciiStreamsetBigDecimal,getBigDecimalsetBinaryStreamsetBlob,getBlobsetBoolean,getBoo......
  • 20230629 java.sql.DatabaseMetaData
    介绍java.sql.DatabaseMetaDatapublicinterfaceDatabaseMetaDataextendsWrapper数据库的元数据API常量procedureResultUnknown:0procedureNoResult:1procedureReturnsResult:2procedureColumnUnknown:0procedureColumnIn:1procedureColumnInOut:2p......
  • 20230629 java.sql.ParameterMetaData
    介绍java.sql.ParameterMetaDatapublicinterfaceParameterMetaDataextendsWrapper预备语句参数的元数据API常量parameterNoNulls:0parameterNullable:1parameterNullableUnknown:2parameterModeUnknown:0parameterModeIn:1parameterModeInOut:2par......
  • 20230629 java.sql.PreparedStatement
    介绍java.sql.PreparedStatementpublicinterfacePreparedStatementextendsStatement预备语句APIpublicaddBatchclearParametersgetMetaData结果集元数据ResultSetMetaDatagetParameterMetaData预备语句参数的元数据ParameterMetaDataexecutee......
  • 20230629 java.sql.ResultSetMetaData
    介绍java.sql.ResultSetMetaDatapublicinterfaceResultSetMetaDataextendsWrapper结果集的元数据API常量columnNoNulls:0columnNullable:1columnNullableUnknown:2publicgetColumnCount返回当前ResultSet对象中的列数getColumnDisplaySize返......
  • 20230628 java.net.URLDecoder
    介绍java.net.URLDecoderpublicclassURLDecoderURL解码器对应的URL编码器类是URLEncoderURL编码模式保留字符A到Z、a到z、0到9,以及.-~_用+字符替换所有的空格将其他所有字符编码为UTF-8,并将每个字节都编码为%后面紧跟一个两位的十六进制数......