Day22-1.Lock锁 、死锁 、线程的生命周期 、线程池

本文详细介绍了Java中的Lock锁,包括其非公平锁与公平锁的概念及ReentrantLock的使用。接着讨论了线程死锁的问题及其示例代码。此外,还阐述了线程的生命周期和如何在Java中获取线程状态。最后,讲解了线程池的重要性和使用方法,如Executors工具类的几种线程池创建方式以及任务提交与关闭线程池的步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 Lock锁

  1. 概述:
    虽然我们可以理解同步代码块和同步方法的锁对象问题,但是并不能直观看到线程在哪里获取了锁,在哪里释放了锁,所以为了更清晰的展示如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock,可以使用此对象,来表示锁的创建和释放。

  2. 特点:
    (1)Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化

  3. 构造方法:
    ReentrantLock():创建一个ReentrantLock的实例
    Lock锁的默认方式、同步代码块、同步方法都为非公平锁
            非公平锁的特点:

    多个线程需要执行,需要先获取锁对象,将来cpu去哪一个线程哪一个线程就获取锁,没有方法控制cpu去哪执行,所以cpu可能一直待在一个线程上执行,就导致某一条线程一直执行,其他线程无法执行。

    Lock锁可以设置为公平锁(在创建对象时,传递一个参数为true):

    多个线程需要执行,需要先获取锁对象,如果cpu去某一个线程A执行,那么其他线程就需要等待锁,等待执行,如果A线程执行之后,就会释放锁对象,下次cpu会选择去等待的线程中执行,而不是一直被某一个线程霸占,就说明每一条线程都有执行的机会。

  4. 加锁解锁方法:
    void lock():获得锁
    void unlock():释放锁

代码

package demos2;

import java.util.concurrent.locks.ReentrantLock;

public class Demo01 {public static void main(String[] args) {

        SellTickets st = new SellTickets();

        Thread t1 = new Thread(st,"窗口1");
        Thread t2 = new Thread(st,"窗口2");
        Thread t3 = new Thread(st,"窗口3");

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

class SellTickets implements Runnable{
    int tickets = 100;
    //获取了一个Lock锁对象
    //如果传入的参数为false,或者不传入参数,都是为非公平锁
    //如果掺入的参数为true,表示当前锁是一个公平锁
    ReentrantLock r = new ReentrantLock(true);
    @Override
    public void run() {
        while(true){
            try{
                //在此获取锁对象
                r.lock();
                if(tickets <= 0){
                    break;
                }
                tickets = tickets - 1;
                System.out.println(Thread.currentThread().getName() + "卖出一张票,还剩" + tickets + "张票");
            }finally {
                //在此释放锁对象
                r.unlock();
            }
        }
    }
}

2 死锁(了解)

死锁概述:

线程死锁是指由两个或者多个线程互相持有并且需要对方的的资源,导致这些线程处 于等待状态,无法前往执行

锁对象:x y

线程A:线程A想要执行,需要持有锁x和锁y 线程A拥有锁x ,需要y
线程B:线程B想要执行,需要持有锁x和锁 线程B拥有锁y,需要x

图示:
在这里插入图片描述

代码

package demos2;

public class Demo02 {
    public static void main(String[] args) {

        String s1 = "左筷子";
        String s2 = "右筷子";

        Thread t = new Thread(){
            public void run(){
                synchronized (s1){
                    System.out.println("张举获取到了" + s1 + ",需要" + s2);
                    synchronized (s2){
                        System.out.println("张举获取到了" + s2 + ",已经拿到一双筷子,	                        可以疯狂开吃!!!");
                    }
                }
            }
        };
        Thread t2 = new Thread(){
            public void run(){
                synchronized (s2){
                    System.out.println("王楠获取到了" + s2 + ",需要" + s1);
                    synchronized (s1){
                        System.out.println("王楠获取到了" + s1 + ",已经拿到一双筷子,	                     可以疯狂开吃!!!");
                    }
                }
            }
        };
        t.start();
        t2.start();
    }
}

3 线程的生命周期

  1. 生命周期:表示一个物体从创建到消亡的过程
  2. 线程从创建到销毁的过程就是线程的生命周期
    在线程的生命周期中,有很多的状态来描述线程
  3. 状态的罗列:
    新建态:线程对象刚刚创建
    就绪态:线程已经启动,等待cpu的执行
    运行态:正在被运行的状态
    阻塞态:通过某些情况,导致线程无法执行(cpu来临也无法执行)
    等待锁对象 线程休眠(sleep wait) 死锁 IO
    死亡态(消亡态):线程正常执行结束 ,线程被意外终止
  4. 图示:
    在这里插入图片描述

3.1 在Java程序中如何获取线程的状态

  1. 概述:
    刚刚所讲的状态,都是理论分析的有关线程的状态。在程序中可以通过一个方法,来获取具体的线程状态。
  2. 获取线程状态的方法:
    getState()
    返回返回值类型为一个内部类:Thread.State,该内部类是通过枚举来定义的。在枚举类型中,定义了六个对象,程序中描述线程的状态都是通过这个六个对象来定义的。
  3. 状态对象罗列:
项目Value
NEW新建态
RUNNABLE线程的运行态和就绪态
BLOCKED阻塞态:由IO数据或者等待锁产生的阻塞态
WAITING阻塞态:通过调用wait方法产生的阻塞态
TIMED_WAITING阻塞态:使用sleep()休眠产生的阻塞态

TERMINATED:死亡态

代码

package demos2;

public class Demo03 {
    public static void main(String[] args) throws InterruptedException {

        Thread t = new Thread(){
            public void run(){
                for(int i = 1;i <= 10;i++){
                    System.out.println(i);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        //线程刚创建,没启动//新建态
        Thread.State s1 = t.getState();
        System.out.println(s1);

        t.start();
        Thread.sleep(10);
        //1、新线程启动之后,就立即获取状态:就绪态
        //2、新线程启动之后,先运行再获取:运行态
        //3、新线程运行结束了,获取:死亡态
        //4、新线程正在启动,获取:阻塞态
        System.out.println(t.getState());
    }
}

4 线程池

  1. 概念:用来存储线程的一个容器

  2. 使用线程池的原因:
    如果java不支持线程池的:

    想要使用线程完成一个任务:需要创建一个线程对象,将任务提交给线程执行。线程将任务完成之后,该线程对象就会被销毁。如果程序中有很多耗时比较短的任务,就降低了任务执行的效率,而且浪费线程资源。

    如果需要完成一个任务,这个任务中有一些意外问题导致线程会意外终止,就导致任务无法正常完成。

    有线程池的使用:

    想要完成一个任务,只需要从线程池中获取即可,节约时间;线程池中的线程做完任务之后,不会被销毁,而是在线程池中继续维护,等待执行下一个任务,节约线程资源。

    如果线程池中的线程再执行任务时,被任务意外终止,线程池会继续提供一下个线程完成该任务,可以保证任务一定完成。

4.1 线程池的使用

  1. 步骤:
    (1)获取线程池对象
    (2)创建任务类对象
    (3)将任务对象提交给线程池
    (4)关闭线程池对象
  2. 获取线程池对象:
    Executors:工具类 获取线程池的工具类
函数Value
newSingleThreadExecutor()获取一个具有单个线程的线程池对象
newFixedThreadPool(int n)获取一个有n条线程的线程池对象
newCachedThreadPool()获取一个线程池对象,根据提交的任务分配线程
  1. 将任务提交给线程池对象:
    submit(Runnable r)
    (1)如果线程池中的线程比较少,任务比较多,多余的任务需要等待其他线程结束 之后再执行。
    (2)如果线程池中的线程比较多,任务比较少,多余的线程等待提交任务
    (3)如果线程池中的线程和任务一样多,多个线程都并发执行任务
  2. 关闭线程池的方法:
    shutdown():将已经提交的任务全部完成之后,再关掉线程池
    shutdownNow():将正在执行的任务全部完成之后,就立即关闭线程池

代码

package demos2;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo04 {
    public static void main(String[] args) {
        //获取一个线程池对象
        //1、当前线程池中只有一条线程
//        ExecutorService single = Executors.newSingleThreadExecutor();
        //1、获取一个线程池,该线程池中有执行条线程
        ExecutorService three = Executors.newFixedThreadPool(3);
        //1、获取一个线程池,该线程池中的线程个数,根据提交的任务分配
//        ExecutorService cached = Executors.newCachedThreadPool();
        //2、准备任务
        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                for(int i = 1;i <= 10;i++){
                    System.out.println(i + "..." + Thread.currentThread().getName());
                }
            }
        };
        Runnable r2 = new Runnable() {
            @Override
            public void run() {
                for(int i = 100;i <= 110;i++){
                    System.out.println(i + "----" + Thread.currentThread().getName());
                }
            }
        };
        Runnable r3 = new Runnable() {
            @Override
            public void run() {
                for(int i = 1000;i <= 1010;i++){
                    System.out.println(i + "*******" + Thread.currentThread().getName());
                }
            }
        };
        Runnable r4 = new Runnable() {
            @Override
            public void run() {
                for(int i = 1000;i <= 1010;i++){
                    System.out.println(i + "^_^_^_^_^_^_^_^_^_^_^" + Thread.currentThread().getName());
                }
            }
        };
        //3、提交任务给线程池
        three.submit(r1);
        three.submit(r2);
        three.submit(r3);
        three.submit(r4);
        //4、关闭线程池
//        three.shutdown();//将提交的任务做完之后,再关闭
        three.shutdownNow();//将正在执行的任务做完之后,就立即关闭
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值