死锁

一个死锁案例

public class DeadLock {
    public static void main(String[] args) {
        Makeup t1 = new Makeup("xm", 0);
        Makeup t2 = new Makeup("xf", 1);
        t1.start();
        t2.start();
    }
}

class Lipstick {

}

class Mirror {

}

class Makeup extends Thread {
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();
    String name;
    int choice;
    public Makeup(String name, int choice) {
        this.name = name;
        this.choice = choice;
    }
    @Override
    public void run() {
        try {
            makeup();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    public void makeup() throws InterruptedException {
        if (choice == 0) {
            synchronized (lipstick) {
                System.out.println(this.getName() + "获得lipstick");
                Thread.sleep(1000);
                synchronized (mirror) {
                    System.out.println(this.getName()+"获得mirror");
                }
            }
        } else {
            synchronized (mirror) {
                System.out.println(this.getName() + "获得mirror");
                Thread.sleep(2000);
                synchronized (lipstick) {
                    System.out.println(this.getName()+"获得lipstick");
                }
            }
        }
    }
}

产生死锁的四个必要条件:

  1. 互斥条件:一个资源每次只能被一个进程使用。
  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
  3. 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
  4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

上面列出了死锁的四个必要条件,我们只要想办法破其中的任意一个或多个条件就可以避免死锁发生

Lock锁

  • 从JDK5.0开始,Java提供了更强大的线程同步机制一通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当
  • java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象
  • ReentrantLock(可重入锁)类实现了Lock,它拥有与synchronized 相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。
public class TestLock {
    public static void main(String[] args) {
        BuyTicket1 station = new BuyTicket1();
        Thread t1 = new Thread(station, "张三");
        Thread t2 = new Thread(station, "李四");
        Thread t3 = new Thread(station, "王五");
        t1.start();
        t2.start();
        t3.start();
    }
}

class BuyTicket1 implements Runnable {
    private int ticket = 10;
    private boolean flag = true;

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

    // 买票
    public void buy()  {
        if (ticket <= 0) {
            flag = false;
            return;
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName()+"获得"+ticket--);
    }
    @Override
    public void run() {
        while (flag) {
            try {
                lock.lock();    // 加锁
                buy();
            } finally {
                lock.unlock();
            }
        }
    }
}

synchronized 与 Lock 的对比

  • Lock是显式锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放
  • Lock只有代码块锁,synchronized有代码块锁和方法锁
  • 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性 (提供更多的子类)
  • 优先使用顺序:
    • Lock > 同步代码块(已经进入了方法体,分配了相应资源)> 同步方法(在方法体之外)
posted @ 2025-11-03 14:03  Huaixuxm  阅读(10)  评论(0)    收藏  举报