死锁&线程通信&线程状态

本文详细介绍了Java中死锁的概念、原因以及避免方法,通过实例展示了如何创建和避免死锁。接着讨论了线程通信,包括wait、notify的区别,并给出一个线程同步的例子。此外,还探讨了线程的状态和线程池的工作原理,包括如何创建线程池以及不同类型的线程池配置。
摘要由CSDN通过智能技术生成

目录

一、死锁

1.1 什么是死锁?

1.2 发生死锁的原因?

 1.3 如何避免死锁产生?

二、线程通信

2.1案例:

 2.2 Sleep和wait的区别?

三、线程的状态 

3.1线程的状态 

四、线程池

4.1 线程池的概念

4.2 线程池的原理

 4.3创建线程池


一、死锁

1.1 什么是死锁?

  • 当第一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁。
  • 一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,由此可能造成死锁。

1.2 发生死锁的原因?

 1.3 如何避免死锁产生?

编写一个将导致死锁的Java程序:A有画板,B有画笔,拿到对方的工具进行画画。

编写锁对象:

public class LockObject {
    public static Object HuaBan = new Object();
    public static Object HuaBi = new Object();
}

编写画板类:

public class HuaBan implements Runnable{
    @Override
    public void run() {
        synchronized (LockObject.HuaBi){
            System.out.println("A拿到画笔");
            synchronized (LockObject.HuaBan){
                System.out.println("A拿到了画板");
                System.out.println("A开始画画");
            }
        }
    }
}

编写画笔类:

public class HUaBi implements Runnable{
    @Override
    public void run() {
        synchronized (LockObject.HuaBan){
            System.out.println("B拿到画板");
            synchronized (LockObject.HuaBi){
                System.out.println("B拿到画笔");
                System.out.println("B开始画画");
            }
        }
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        HuaBan huaBan = new HuaBan();
        HUaBi hUaBi = new HUaBi();
        Thread thread1 = new Thread(huaBan);
        Thread thread2 = new Thread(hUaBi);

        thread1.start();
        thread2.start();
    }
}

运行结果:

以上案例就导致了死锁现象。

如何避免死锁现象?

  1. 尽量不要使用锁嵌套;
  2. 不要使用锁,而使用安全锁(比如:java.util.concurrent下的类);
  3. 设置锁的超时时间。lock锁,到达指定时间没有获取锁,则不会再等待该锁。

 使用第三种方法解决死锁案例:


public class HuaBan implements Runnable{
    @Override
    public void run() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        synchronized (LockObject.HuaBi){
            System.out.println("A拿到画笔");
            synchronized (LockObject.HuaBan){
                System.out.println("A拿到了画板");
                System.out.println("A开始画画");
            }
        }
    }
}

运行结果:

二、线程通信

等待:

  • public final void wait()
  • public final void wait(long timeout)
  • 必须咋子对obj加锁的同步代码块中。在一个线程中,调用obj.wait()时,此线程会释放其拥有的所有锁标记。同时此线程阻塞在o的等待队列中。释放锁,放入等待队列。

通知: 

  • public final void notify()
  • public final void notifyAll()

2.1案例:

一张银行卡,小红取钱,小明存钱。

定义银行卡:


public class BankCard {
    private double balance;//定义银行卡余额
    private boolean flag;//true表示有钱,  false表示没钱

    public synchronized void save(double money) {
        if (flag) {//有钱,进入等待队列
            try {
                wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        balance = balance + money;
        System.out.println(Thread.currentThread().getName() + "往卡中存入" + money + "元,卡中余额" + balance + "元");
        flag = true;
        //唤醒等待队列中的线程
        notify();
    }

    public synchronized void take(double money) {
        if (flag == false) {
            try {
                wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        balance = balance - money;
        System.out.println(Thread.currentThread().getName() + "从卡中取出" + money + "元,卡中余额" + balance + "元");

        flag = false;
        //唤醒等待队列中的线程
        notify();
    }
}

public class TakeRunnable implements Runnable{
    private BankCard bankCard;
    public TakeRunnable(BankCard card){
        bankCard = card;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            bankCard.take(1000);
        }
    }
}

public class SaveRunnable implements Runnable{
    private BankCard bankCard;
    public SaveRunnable(BankCard card){
        bankCard = card;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            bankCard.save(1000);
        }
    }
}

测试类:


public class Test {
    public static void main(String[] args) {
        BankCard bankCard = new BankCard();

        SaveRunnable save = new SaveRunnable(bankCard);
        TakeRunnable take = new TakeRunnable(bankCard);

        Thread t1 = new Thread(save, "小明");
        Thread t2 = new Thread(take, "小红");

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

部分代码运行结果:

小明往卡中存入1000.0元,卡中余额1000.0元
小红从卡中取出1000.0元,卡中余额0.0元
小明往卡中存入1000.0元,卡中余额1000.0元
小红从卡中取出1000.0元,卡中余额0.0元
小明往卡中存入1000.0元,卡中余额1000.0元
小红从卡中取出1000.0元,卡中余额0.0元

 2.2 Sleep和wait的区别?

  1.  wait需要使用notify或notifyAll唤醒,而sleep到时间自动唤醒。
  2.  wait来自于Object类中,sleep来自Thread类中
  3.  wait会是否锁资源,sleep不会释放锁资源。
  4.  wait必须放在同步代码中,而sleep可以放在任意位置。

三、线程的状态 

3.1线程的状态 

  1. NEW:新建状态。当new一个线程对象时进入该状态。
  2. RUNNABLE:运行状态。调用完start并获取cpu时间片进入运行状态。
  3. BLOCKED:堵塞状态,当没有获取锁资源时。
  4. WAITING:当执行了wait方法时,该线程进入等待状。
  5. TIME_WAITING:当执行sleep方法时,进入该状态。
  6. TERMINATED:终止。当线程执行完毕或出现异常中断。

四、线程池

4.1 线程池的概念

问题:

  • 线程时宝贵的内存资源、单个线程约占1MB空间,过多分配易造成内存溢出。
  • 频繁的创建及销毁线程会增加虚拟机回收频率、资源开销,造成程序性能下降。

线程池:

  •  线程容器,可设定线程分配的数量上限。

  • 将预先创建的线程对象存入池中,并重用线程池中的线程对象。

  • 避免频繁的创建和销毁。

4.2 线程池的原理

 4.3创建线程池

(1)常用的线程池接口和类(所在包Java.util.concurrent):

  • Executor:线程池的顶级接口。该接口中就存在一个方法。void execute(Runnable command);执行线程任务的方法。
  • ExecutorService:线程池接口,可通过submit(Runnable task)提交任务代码。
  1.   1. shutdown():关闭线程池---当前线程池有还要任务,需要等任务完后后才会关闭。
      2. shutdownNow(): 立刻关闭线程池。
      3. isShutdown(): 是否属于关闭状态。
      4. isTerminated(): 判断是否线程池终止了
      5. submit(Callable<T> task): 执行线程任务。
         submit(Runnable task);
  • Executors工厂类:通过此类可以获得一个线程池。
  • 通过newFixedThreadPool(int nThreads) 获取固定数量的线程池。参数:指定线程池中线程的数量。
  • 通过newCachedThreadPool() 获的动态数量的线程池,如不够则创建新的,没有上限
public class Test {
    public static void main(String[] args) {
        //获取固定长度的线程池。---阿里巴巴不建议使用Executors创建线程池
      //  ExecutorService executorService = Executors.newFixedThreadPool(5);
        //创建单一线程池---池子中只有一个线程的对象。---适合任务的有序执行。
        //ExecutorService executorService = Executors.newSingleThreadExecutor();
        //可变线程池
        //ExecutorService executorService = Executors.newCachedThreadPool();
        //延时线程池
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
         executorService.schedule(new Runnable() {
             @Override
             public void run() {
                 System.out.println("定时执行任务");
             }
         },3, TimeUnit.SECONDS);

//        for (int i = 0; i < 200; i++) {
//            executorService.submit(new Runnable() {
//                @Override
//                public void run() {
//                    System.out.println(Thread.currentThread().getName()+"--------------------");
//                }
//            });
//        }
        executorService.shutdown();
    }
}

(2)自定义参数线程池

(1)没有无参构造函数 (2)构造函数私有化 
int corePoolSize,核心线程数 
int maximumPoolSize,最大的个数
long keepAliveTime,等待时间长 
TimeUnit unit,时间单位---
BlockingQueue<Runnable> workQueue: 等待队列


public class Test {
    public static void main(String[] args) {
    BlockingQueue blockingQueue = new ArrayBlockingQueue(3);
        ExecutorService executorService = new ThreadPoolExecutor(2, 5, 10,TimeUnit.SECONDS, blockingQueue);

        for (int i = 0; i < 19; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"-------------");
                }
            });
        }
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值