IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java多线程_10 线程的锁 -> 正文阅读

[Java知识库]Java多线程_10 线程的锁


死锁

死锁定义
死锁是指两个或多个进程由于竞争资源而造成的一种僵局,若无外力作用,这些进程无法向前推进。

死锁产生原因

  • 竞争资源:竞争不可抢占资源或可消耗性资源时可能会导致死锁。
  • 进程推进顺序不当;

产生死锁必要条件
必要条件:四个条件都满足,才能发生死锁,也必然能发生死锁。

  • 互斥条件:进程之间必须互斥使用某些资源才可能引起死锁;
  • 请求保持:进程已经占有至少一个资源,又提出新的资源请求;
  • 不可抢占:进程已经占有的资源不能被剥夺;
  • 循环等待:死锁时必然形成一个进程——资源环形链。

线程的死锁

当一个线程永远地持有一个锁,并且其它线程都尝试去获得这个锁时,那么它们将永远被阻塞。

如果线程A持有锁L并且想获得锁M,线程B持有锁M并且想获得锁L,那么这两个线程将永远等待下去,这种情况就是最简单的死锁形式。

public class DeadLock {
	//定义两个资源
    private final Object left = new Object();
    private final Object right = new Object();

    public void leftRight() throws Exception{
        synchronized (left){
            //让线程休眠,否则先运行的线程有可能获得两个锁了
            Thread.sleep(2000);
            synchronized (right){
                System.out.println("leftRight end!");
            }
        }
    }

    public void rightLeft() throws Exception{
        synchronized(right){
            Thread.sleep(2000);
            synchronized (left){
                System.out.println("rightLeft end!");
            }
        }
    }
}

定义两个依赖该资源的线程:

public class Thread0 extends Thread{
    private DeadLock dl;

    public Thread0(DeadLock dl){
        this.dl = dl;
    }
    @Override
    public void run() {
        try {
            dl.leftRight();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class Thread1 extends Thread {
    private DeadLock dl;

    public Thread1(DeadLock dl){
        this.dl = dl;
    }

    @Override
    public void run() {
        try {
            dl.rightLeft();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
/**
 * @Author: 凤文  
 * @CreateTime: 2021/11/27 13:32
 * @Description: 线程的死锁
 * 本例结果是 System.out.println("leftRight end!")结果迟迟不出来,程序一直在运行
 * 这时因为执行它们所需要的对象被这两个线程锁住了,都拿不到自己需要的对象,这就是死锁。
 */
public class Test {
    public static void main(String[] args) {
        DeadLock dl = new DeadLock();
        new Thread0(dl).start();
        new Thread1(dl).start();
    }
}

我们在启动这两个线程的时候,任务中需要left和right的资源,刚开始的时候两个线程各抢到一个资源,假如Thread0线程抢到了left,Thread1线程抢到了right。两秒后,两个线程需要继续推进,Thread0需要拿到right资源才能向前推进,此时right资源被Thread1占用着,所以Thread0只能等待,Thread1也是同理,两个线程的所需要的资源都被对方持有且不放,导致两个线程一直等待,线程无法向前推进,这就是一个最简单的死锁。

线程的明锁

Java5提供了锁对象Lock,利用锁可以方便地实现资源的封锁,用来对竞争资源并发访问的控制。Lock所有加锁和解锁的方法都是显示的。

方法说明
Lock.lock()获取锁
Lock.unlock()释放锁
Lock可以构建公平锁和非公平锁,默认是非公平锁。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author: 凤文
 * @CreateTime: 2021/11/27 13:59
 * @Description:
 * 我们可以在operator方法中加synchronized关键字
 * 对于明锁,我们还可以使用Lock也能达到同样的目的
 */
public class Count {
    private int num;

    /**
     * 锁对象
     */
    private Lock lock = new ReentrantLock();

    public Count(int num) {
        this.num = num;
    }

    public void operator(int operatorNum) {
        lock.lock();
        this.num += operatorNum;
        System.out.println(Thread.currentThread().getName()
                + "操作的数量是" + operatorNum + "," + "操作后现在的数量是:" + num);
        lock.unlock();
    }
}
public class ThreadOperator extends Thread{
    private int operatorNum;

    private Count c;

    public ThreadOperator(int operatorNum, Count c,String threadName) {
        super(threadName);
        this.operatorNum = operatorNum;
        this.c = c;
    }

    @Override
    public void run() {
        c.operator(operatorNum);
    }
}
/**
 * @Author: 凤文
 * @CreateTime: 2021/11/27 13:56
 * @Description: 线程的明锁
 * synchronized是Java关键字,是内置的特性,属于隐式的锁
 * 而Java中还有明锁,提供了锁对象Lock,所有的加锁和解锁的方法都是显示的。
 * Lock().lock():获取锁
 * Lock().unlock():释放锁
 *
 * 本例中初值100,经过5个线程的操作,结果应该为130
 * 如果不加锁肯定达不到预期
 * 我们可以在operator方法中加synchronized关键字
 * 对于明锁,我们还可以使用Lock也能达到同样的目的
 *
 */
public class Test {
    public static void main(String[] args) {
        Count c = new Count(100);

        ThreadOperator thread1 = new ThreadOperator(10,c, "线程A");
        ThreadOperator thread2 = new ThreadOperator(-20,c, "线程B");
        ThreadOperator thread3 = new ThreadOperator(30,c, "线程C");
        ThreadOperator thread4 = new ThreadOperator(-40,c, "线程D");
        ThreadOperator thread5 = new ThreadOperator(50,c, "线程E");

        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();
    }
}

线程的公平锁与非公平锁

Java的ReenTrantLock也就是用队列实现的公平锁和非公平锁:

在公平的锁中,如果有另一个线程持有锁或者有其他线程在等待队列中等待这个锁,那么新发出的请求的线程将被放入到队列中。

而非公平锁上,只有当锁被某个线程持有时,新发出请求的线程才会被放入队列中(此时和公平锁是一样的)。所以,它们的差别在于非公平锁有更多的机会去抢占锁。

模拟售票系统:

/**
 * @Author: 凤文
 * @CreateTime: 2021/11/27 14:44
 * @Description: 公平锁与非公平锁、模拟购票系统
 */
public class UserRun implements Runnable {
    //一共有10张票
    private int count = 10;

    private boolean flag = true;

    //true代表公平锁,false就是非公平锁
    private Lock lock = new ReentrantLock(true);

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + ",欢迎订票");
        while (flag) {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "取到了第" + count-- + "张票");
            if (count < 1) {
                flag = false;
            }
            lock.unlock();
        }
        System.out.println(Thread.currentThread().getName() + "谢谢您");
    }

}
/**
 * @Author: 凤文
 * @CreateTime: 2021/11/27 14:50
 * @Description: 公平锁与非公平锁
 * 当使用公平锁时,每一个线程都要到队列里等着,等资源锁释放时,按顺序来,也就是按照start()调用的顺序
 * 而非公平锁不看谁先来,所以非公平锁更有机会获得锁
 */
public class Test {
    public static void main(String[] args) {
        UserRun run = new UserRun();

        Thread t1 = new Thread(run,"东门汽车站");
        Thread t2 = new Thread(run,"南门汽车站");
        Thread t3 = new Thread(run,"北门汽车站");

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

    }
}

公平锁的结果:
在这里插入图片描述
对于公平锁,我们发现它是有顺序的,按照队列中的顺序执行。

将锁换成非公平锁

    private Lock lock = new ReentrantLock(false);

在这里插入图片描述
对于非公平锁我们发现线程是可以抢占资源的,而不是按顺序来的。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-28 11:39:48  更:2022-04-28 11:42:44 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:43:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码