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

死锁(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.

参考文章

猜你喜欢

转载自blog.csdn.net/mouday/article/details/132467714