Java线程详解及多线程线程池使用与原理

一、Java线程

本文主要介绍线程的基本概念与创建方式和多线程线程池的详解以及使用

1.1 什么是线程

要了解线程首先我们要知道什么是进程,进程与线程之间的关系

  • 进程
    进程是操作系统进行资源分配和调度的一个独立单位, 系统中每一个运行的程序都是一个进程,每个进程都有自己独立的内存空间。
  • 线程
    线程是操作系统进行运算调度的最小单位, 被包含在进程中,每个进程可以同时拥有多个线程。

1.2 线程创建

线程的创建有三种方式: 继承Thread类、实现Runnable接口、实现Callable接口

  • 继承Thread,重写run方法
public class TestThread extends Thread{
    
    
    private int count=0;
    @Override
    public void run() {
    
    
        //需要线程执行的逻辑
        for (int i = 0; i < 10; i++) {
    
    
            System.out.println(count++);
        }
    }
    public static void main(String[] args) {
    
    
        TestThread testThread = new TestThread();
        //启动线程
        testThread.start();
    }
}
  • 实现Runnable接口,重写run方法
public class TestThread1 implements Runnable{
    
    
    private int count=0;
    @Override
    public void run() {
    
    
        for (int i = 0; i < 10; i++) {
    
    
            System.out.println(count++);
        }
    }
    public static void main(String[] args) {
    
    
        TestThread1 testThread1 = new TestThread1();
        //把实现了runable接口的实现类对象作为参数,放到thread的构造方法
        Thread thread = new Thread(testThread1);
        //启动线程
        thread.start();
    }
}
  • 实现Callable接口,重写call方法
//Callable接口与Runnable接口不同的是Callable接口的call方法是具有返回值的,
//如果有需要线程具有返回数据的情况使用callable
public class TestThread2 implements Callable {
    
    
    private int count = 0;
    @Override
    public Object call() throws Exception {
    
    
        for (int i = 0; i < 10; i++) {
    
    
            System.out.println(count++);
        }
        return count;
    }
public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        TestThread2 testThread2 = new TestThread2();
        FutureTask task = new FutureTask(testThread2);
        Thread thread = new Thread(task);
        thread.start();
        Object o = task.get();
        System.out.println("count值为:"+o);

    }
}

1.3 线程状态

线程具有五种状态分别是新建、就绪、运行、阻塞、死亡。

  • 新建:当我们new Thread()对象时,该线程对象就处于新建状态
  • 就绪:当我们调用start()方法时,该线程进入就绪状态,此时的线程进入到试图获取cpu资源的状态。
  • 运行:当就绪线程获取到cpu执行权时,线程进入到运行状态。
  • 死亡:当线程正常执行结束或者调用stop方法后,线程进入死亡状态。
  • 阻塞:当线程进入睡眠,等待,或因获取锁被阻塞时进入到阻塞状态。
    当调用Thread类的sleep方法时,线程进入到睡眠状态,在睡眠指定时间后进入到就绪状态
    当调用Thread类的yield等方法时,线程会让步其它线程执行,线程进入到等待状态

二、多线程线程池

  • 为什么要使用线程池?
    在需要使用大量线程的业务场景,我们需要频繁的创建线程,当线程执行完需要进行销毁线程,每当有新任务我们都需要重新创建线程,这样频繁的创建销毁线程会大大占用系统开销。
  • 使用线程池有哪些优势?
    降低资源消耗,通过重复利用已创建线程降低线程创建和销毁造成的消耗。
    提高响应速度,当任务到达时,任务可以不需要等待线程创建就能立即执行。
    提高线程可管理性,线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

2.1 线程池工作原理

在这里插入图片描述

2.2 ThreadPoolExecutor详解

  • 如何使用线程池?
    要使用线程池我们先了解ThreadPoolExecutor类
    该类具有四个构造方法,另外三个都会调用其中一个,让我们看看这个构造方法包含哪些参数
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 

1、corePoolSize: 核心线程数,该线程池可以创建的执行任务的核心线程数量
2、maximumPoolSize:最大线程数,指当corePoolSize与workQueue存储满时,线程池会创建非核心线程执行任务,maximumPoolSize为线程池可存活最大线程数。
3、keepAliveTime:最大等待时间,指非核心线程数的等待时间, 核心线程被创建后会永久存在等待任务到来,而非核心线程执行完任务会被销毁, 当空闲时间超过keepAliveTime,则会销毁非核心线程
4、unit:keepAliveTime的时间单位
//TimeUnit.DAYS; 天
//TimeUnit.HOURS; 小时
//TimeUnit.MINUTES; 分钟
//TimeUnit.SECONDS; 秒
//TimeUnit.MILLISECONDS; 毫秒
//TimeUnit.MICROSECONDS; 微妙
//TimeUnit.NANOSECONDS; 纳秒
5、workQueue:等待队列,当线程池内线程数达到corePoolSize,则会将任务放于等待队列
常见的等待队列有以下三种类型
1)SynchronousQueue 直接提交策略
2)LinkedBlockingQueue 无界队列
3)ArrayBlockingQueue:、基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序
该队列当核心线程数达到最大值,新提交任务会存入队列,队列存储满会新建非核心线程,当总线程数达到maximumPoolSize,会使用拒绝策略来处理新提交任务。

  1. SynchronousQueue 直接提交策略

  2. LinkedBlockingQueue 无界队列
    该等待队列为无界队列,也就是会无限制添加,永远不会创建非核心线程,新加入任务永远会等待核心线程执行。

  3. ArrayBlockingQueue 有界队列
    该队列当核心线程数达到最大值,新提交任务会存入队列,队列存储满会新建非核心线程,当总线程数达到maximumPoolSize,会使用拒绝策略来处理新提交任务。

  4. PriorityBlockingQueue 具有优先级的无界队列

6、threadFactory :自定义线程工厂
7、handler :拒绝策略,线程池中线程数量超过最大值maximumPoolSize时调用的拒绝策略,常见有以下几种
1)AbortPolicy:中止策略。默认的拒绝策略,直接抛出 RejectedExecutionException
2)DiscardPolicy:直接抛弃请求过来的任务
3)DiscardOldestPolicy:抛弃队列中最先达到任务,然后将本次任务添加到队列
4)CallerRunsPolicy:由调用者线程执行该任务。即占用向线程池发送任务的线程,占用主线程,可以缓解线程池内部压力。

  • Executors 提供几个常见创建线程池方法
    1)newFixedThreadPool:固定线程数的线程池。核心线程数等于最大线程数,工作队列使用无界的LinkedBlockingQueue。适用于为了满足资源管理的需求,而需要限制当前线程数量的场景,适用于负载比较重的服务器。
    2)newSingleThreadExecutor:只有一个线程的线程池,即单线程线程池。适用于需要保证顺序的执行各个任务的场景。
    3)newFixedThreadPool:固定线程线程池,corePoolSize = maximumPoolSize,阻塞队列选取LinkedBlockingQueue,适用于限制当前线程数量的场景。
    4)newCachedThreadPool:该线程池核心线程数为0,最大线程数为 Integer.MAX_VALUE,线程存活时间为60s,该线程池可以无限扩张且会短时间内销毁空闲线程,适用于负载较轻服务器。

猜你喜欢

转载自blog.csdn.net/pgcdnameming/article/details/121784953