Java(多线程Thread)

本文深入探讨了Java中的死锁现象及其产生原因,通过示例代码详细解释了死锁的形成过程。此外,文章全面介绍了ThreadPoolExecutor的工作原理,包括其内部的workQueue任务队列机制,以及如何选择合适的队列类型。还讲解了SynchronousQueue、LinkedBlockingQueue和ArrayBlockingQueue的特点,以及线程池的拒绝策略和监控方法。
摘要由CSDN通过智能技术生成

死锁

public class DeadLock {
    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = new Object();
        Thread t1 = new Thread(new T1(o1,o2));
        Thread t2 = new Thread(new T2(o1,o2));
        t1.start();
        t2.start();
    }
}

class T1 implements Runnable{
    Object o1;
    Object o2;

    T1(Object o1,Object o2){
        this.o1=o1;
        this.o2=o2;
    }

    public void run() {
        synchronized(o1){
            try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
            synchronized(o2){}
        }
    }
}

class T2 implements Runnable{
    Object o1;
    Object o2;

    T2(Object o1,Object o2){
        this.o1=o1;
        this.o2=o2;
    }

    public void run() {
        synchronized(o2){
            try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
            synchronized(o1){}
        }
    }
}

 

 

ThreadPoolExecutor详解

ThreadPoolExecutor的 workQueue 任务队列详解

workQueue :它决定了缓存任务的排队策略。对于不同的应用场景我们可能会采取不同的排队策略,这就需要不同类型的队列。这个队列需要一个实现了BlockingQueue接口的任务等待队列。

在ThreadPoolExecutor线程池的API文档中,一共推荐了三种等待队列,它们是:SynchronousQueue 、LinkedBlockingQueue 和 ArrayBlockingQueue

1、队列和栈

● 队列:是一种特殊的线性结构,允许在线性结构的前端进行删除/读取操作;允许在线性结构的后端进行插入操作;这种线性结构具有“先进先出”的操作特点:
在这里插入图片描述

但是在实际应用中,队列中的元素有可能不是以“进入的顺序”为排序依据的。例如我们将要讲到的 PriorityBlockingQueue 队列。

● 栈:栈也是一种线性结构,但是栈和队列相比只允许在线性结构的一端进行操作,入栈和出栈都是在一端完成。

在这里插入图片描述

2、有限队列

2.1、SynchronousQueue:

“是这样 一种阻塞队列,其中每个 put 必须等待一个 take,反之亦然。同步队列没有任何内部容量。翻译一下:这是一个内部没有任何容量的阻塞队列,任何一次插入操作的元素都要等待相对的删除/读取操作,否则进行插入操作的线程就要一直等待,反之亦然。

SynchronousQueue<Object> queue = new SynchronousQueue<Object>();
// 不要使用add,因为这个队列内部没有任何容量,所以会抛出异常“IllegalStateException”
// queue.add(new Object());
// 操作线程会在这里被阻塞,直到有其他操作线程取走这个对象
queue.put(new Object());

2.2、 ArrayBlockingQueue:

一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。

// 我们创建了一个ArrayBlockingQueue,并且设置队列空间为2
ArrayBlockingQueue<Object> arrayQueue = new ArrayBlockingQueue<Object>(2);
// 插入第一个对象
arrayQueue.put(new Object());
// 插入第二个对象
arrayQueue.put(new Object());
// 插入第三个对象时,这个操作线程就会被阻塞。
arrayQueue.put(new Object());
// 请不要使用add操作,和SynchronousQueue的add操作一样,它们都使用了AbstractQueue中的add实现

3、 无限队列

3.1、 LinkedBlockingQueue:

LinkedBlockingQueue 是我们在 ThreadPoolExecutor线程池中常用的等待队列。它可以指定容量也可以不指定容量。由于它具有“无限容量”的特性,所以我还是将它归入了无限队列的范畴(实际上任何无限容量的队列/栈都是有容量的,这个容量就是Integer.MAX_VALUE)。
LinkedBlockingQueue 的实现是基于链表结构,而不是类似 ArrayBlockingQueue 那样的数组。但实际使用过程中,不需要关心它的内部实现,如果指定了LinkedBlockingQueue 的容量大小,那么它反映出来的使用特性就和 ArrayBlockingQueue 类似了。

LinkedBlockingQueue<Object> linkedQueue = new LinkedBlockingQueue<Object>(2);
linkedQueue.put(new Object());
// 插入第二个对象
linkedQueue.put(new Object());
// 插入第三个对象时,这个操作线程就会被阻塞。
linkedQueue.put(new Object());

3. 拒绝策略-handler

所谓拒绝策略,就是当线程池满了、队列也满了的时候,我们对任务采取的措施。或者丢弃、或者执行、或者其他...

jdk自带4种拒绝策略,我们来看看。

  1. CallerRunsPolicy // 在调用者线程执行
  2. AbortPolicy // 直接抛出RejectedExecutionException异常
  3. DiscardPolicy // 任务直接丢弃,不做任何处理
  4. DiscardOldestPolicy // 丢弃队列里最旧的那个任务,再尝试执行当前任务

这四种策略各有优劣,比较常用的是DiscardPolicy,但是这种策略有一个弊端就是任务执行的轨迹不会被记录下来。所以,我们往往需要实现自定义的拒绝策略, 通过实现RejectedExecutionHandler接口的方式。

关闭线程池

在线程池使用完成之后,我们需要对线程池中的资源进行释放操作,这就涉及到关闭功能。我们可以调用线程池对象的shutdown()shutdownNow()方法来关闭线程池。

这两个方法都是关闭操作,又有什么不同呢?

  1. shutdown()会将线程池状态置为SHUTDOWN,不再接受新的任务,同时会等待线程池中已有的任务执行完成再结束。
  2. shutdownNow()会将线程池状态置为SHUTDOWN,对所有线程执行interrupt()操作,清空队列,并将队列中的任务返回回来。

另外,关闭线程池涉及到两个返回boolean的方法,isShutdown()isTerminated,分别表示是否关闭和是否终止。

线程池监控

如果系统中大量用到了线程池,那么我们有必要对线程池进行监控。利用监控,我们能在问题出现前提前感知到,也可以根据监控信息来定位可能出现的问题。

那么我们可以监控哪些信息?又有哪些方法可用于我们的扩展支持呢?

首先,ThreadPoolExecutor自带了一些方法。

  1. long getTaskCount(),获取已经执行或正在执行的任务数
  2. long getCompletedTaskCount(),获取已经执行的任务数
  3. int getLargestPoolSize(),获取线程池曾经创建过的最大线程数,根据这个参数,我们可以知道线程池是否满过
  4. int getPoolSize(),获取线程池线程数
  5. int getActiveCount(),获取活跃线程数(正在执行任务的线程数)

其次,ThreadPoolExecutor留给我们自行处理的方法有3个,它在ThreadPoolExecutor中为空实现(也就是什么都不做)。

  1. protected void beforeExecute(Thread t, Runnable r) // 任务执行前被调用
  2. protected void afterExecute(Runnable r, Throwable t) // 任务执行后被调用
  3. protected void terminated() // 线程池结束后被调用

针对这3个方法,我们写一个例子。

public class ThreadPoolTest {
    public static void main(String[] args) {
        ExecutorService executor = new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1)) {
            @Override protected void beforeExecute(Thread t, Runnable r) {
                System.out.println("beforeExecute is called");
            }
            @Override protected void afterExecute(Runnable r, Throwable t) {
                System.out.println("afterExecute is called");
            }
            @Override protected void terminated() {
         

 

demo


    private static void  demo2() {
      Executor executor= new ThreadPoolExecutor(5,5,60,TimeUnit.SECONDS,new ArrayBlockingQueue<>(10)){
          @Override protected void beforeExecute(Thread t, Runnable r) {
              System.out.println("beforeExecute is called");
          }
          @Override protected void afterExecute(Runnable r, Throwable t) {
              System.out.println("afterExecute is called");
          }
          @Override protected void terminated() {
              System.out.println("terminated is called");
          }
      };
        Callable callable=new Callable() {
            @Override
            public Object call() throws Exception {
                System.out.println(new Date()+"thread id is: " + Thread.currentThread().getId());
                Thread.sleep(1000*5);
                return null;
            }
        };

        for (int i = 0; i < 10; i++) {
            ((ThreadPoolExecutor) executor).submit(callable);
           long l=  ((ThreadPoolExecutor) executor).getCompletedTaskCount();
          if (l==10){

          }
        }

        while (true) {
            if (((ThreadPoolExecutor) executor).getActiveCount() == 0) {
                System.out.println("over");
                return;

            }
        }

    }


}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值