【Thread】线程的死锁与锁(四)

1. 死锁

多个线程各自占用一些共享资源,并且互相等待其它线程占有的资源才能运行,而导致两个或多个线程都在等待对方释放资源,都停止执行的情形。

某一个同步代码块同时拥有两个以上对象的锁时,就有可能发生死锁的问题。

案例:女生化妆。化妆品有:镜子、口红。灰姑娘先选择口红,再选择镜子;白雪公主先选择镜子,再选择口红

// 口红
class Lipstick {}

// 镜子
class Mirror {}

class Makeup extends Thread {

    private static Lipstick lipstick = new Lipstick();
    private static Mirror mirror = new Mirror();

    private int choice;
    private String girlName;

    public Makeup(int choice, String girlName) {
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        try {
            makeup();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 化妆
    private void makeup() throws Exception{
        if (0 == choice) {
            synchronized (lipstick) {
                System.out.println(this.girlName + "拿到了口红的锁");
                Thread.sleep(1000);
                synchronized (mirror) {
                    System.out.println(this.girlName + "拿到了镜子的锁");
                }
            }
        } else {
            synchronized (mirror) {
                System.out.println(this.girlName + "拿到了镜子的锁");
                Thread.sleep(2000);
                synchronized (lipstick) {
                    System.out.println(this.girlName + "拿到了口红的锁");
                }
            }
        }
    }
}

测试:

// 死锁  多个线程互相持有对方需要的资源,然后形成僵持
public class DeadLock {

    public static void main(String[] args) {
        Makeup g1 = new Makeup(0, "灰姑凉");
        Makeup g2 = new Makeup(1, "白雪公主");
        g1.start();
        g2.start();
    }
}

运行后:
【Thread】线程的死锁与锁(四)
程序一直处于死锁状态。

那么,如何解除这种状态呢?

class Makeup extends Thread {

    ...

    // 化妆
    private void makeup() throws Exception{
        if (0 == choice) {
            synchronized (lipstick) {
                System.out.println(this.girlName + "拿到了口红的锁");
                Thread.sleep(1000);
            }
            synchronized (mirror) {
                System.out.println(this.girlName + "拿到了镜子的锁");
            }
        } else {
            synchronized (mirror) {
                System.out.println(this.girlName + "拿到了镜子的锁");
                Thread.sleep(2000);
            }
            synchronized (lipstick) {
                System.out.println(this.girlName + "拿到了口红的锁");
            }
        }
    }
}

运行结果如下:
【Thread】线程的死锁与锁(四)

2.锁 Lock

从 JDK 1.5 开始,Java 提供了更强大的线程同步机制:通过显示定义同步锁对象来实现同步。同步锁使用 Lock 对象充当。

java.util.concurrent.locks.Lock 接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对 Lock 对象加锁,线程开始访问共享资源之前应先获得 Lock 对象。

ReentrantLock 类实现了 Lock 接口,它拥有与 synchronized 相同的并发性和内存语义。在实现线程安全的控制中,比较常用的是 ReentrantLock,可以显示地加锁、释放锁。

Locksynchronized 的比较:

  1. Lock 显示锁,手动加锁、解锁;用 Lock,JVM 将花费较少的时间来调度线程,性能更好
  2. synchronized 隐式锁,出了作用域自动释放

案例:使用 Lock 实现线程安全地买票

class TestLock2 implements Runnable {

    private int ticketNums = 10;
	// 可重入锁
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            lock.lock();
            try {
                if (ticketNums <= 0) {
                    break;
                }
                try {
                    Thread.sleep(1000);
                    System.out.println(ticketNums--);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } finally {
                lock.unlock();
            }
        }
    }
}

测试:

public class TestLock {

    public static void main(String[] args) {
        TestLock2 testLock2 = new TestLock2();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
    }
}
上一篇:JUC基础学习笔记


下一篇:编码验证——类方法前,加上修饰符 synchronized,同步对象是当前类对象