java线程与线程池

创建线程的三种方式

  1. 继承 Thread 类,重写其 run() 方法
    从源码可知,Thread 类是 Runnable 接口的实现。
  • 属性(少部分属性)
    线程名:char name[]
    优先级:int priority 最大为10,最小为1,默认为5
    是否守护线程:boolean daemon 默认 false
  • 构造方法(只列出常用的)
    构造函数均调用 init 方法,参数不一样
    init(Thread group, Runnable target, String name, long stackSize)
    Thread()
    Thread(Runnable target)
    Thread(String name)
    Thread(Runnable target,String name)
  • 方法(常用)
    currentThread() :返回当前线程
    yield() :让出 cpu ,重回就绪队列等待调度
    sleep(long mills) :休眠 mills 毫秒,让出 cpu ,进入阻塞队列,休眠结束回到就绪队列等待调度。
    join() :在别的线程中被调用,则直到该线程执行完毕调用线程才会执行。
    run() :继承 Thread 类时重写该方法,线程执行内容,如果 Runnable 参数不为空,则调用该 Runnable 对象 target 的 run() 方法。target 可为 Runnable 的实现类实例,也可以是 Thread 的子类实例,因为 Thread 类就是 Runnable 的实现类。
    start() :启动线程方法
    isAlive() :判断当前线程是否处于活动状态
    getId() :获取线程的唯一标识
  1. 实现Runnable接口,重写 run() 方法
    编写了对应类之后,将该类的实例传给 Thread 类的 target 属性,由 Thread 对象调用 start() 方法来执行。
  2. 实现Callable接口,重写 v call() 方法
    其中 v 是返回值,实现 Callable 接口的线程类可以具有返回值,其中 v 即为该线程返回值,用 Future< v > 来接收 v 类型的返回值,并通过其 get() 方法来取出。

停止线程的方法

  1. 线程执行完毕,正常停止
  2. 通过 stop() 方法,不推荐使用
  3. 通过 interrupt() 方法,不过调用只是开启中断标志,并未中断线程
  • interrupt() 方法
    interrupt() :开启中断标志
    interrupted() :判断当前线程是否已经中断(类方法)
    isInterrupted() :判断线程是否已经中断(实例方法)
    使用方案:
  1. 判断是否已经中断,是则抛出中断异常:throw new InterruptedException() 要记得 catch 异常
//in the run()
if (this.interrupted()) {
    throw new InterruptedException();
}
  1. 调用 sleep(long) 方法之后立即调用 interrupt() 方法,休眠被中断,线程终止

    MyThread 线程类中的 run() 中的代码:
// 线程休眠20秒
try {
    Thread.sleep(20000);
} catch (InterruptedException e) {
    System.out.println("线程在沉睡中被停止,进入catch!");
    e.printStackTrace();
}

启动该线程的 mian() 方法中代码:

// 主线程中休眠 2秒, 然后调用上面线程的 interrupt() 方法
    MyThread thread = new Mythread();
    thread.start();
    Thread.sleep(2000);
    thread.interrupt();
    System.out.println("end!");
  1. 判断已经中断时,直接用 return ;语句终止线程

暂停线程

在java多线程中,使用 suspend() 方法暂停,使用 resume() 方法恢复线程的执行。

线程状态转移图

下图截自《java多线程编程核心技术》一书
方法与状态关系示意图
从图中可知,线程一共有六种状态:
NEW:线程刚创建出来的状态
RUNNABLE:线程启动,即执行了 start() 方法之后,进入运行状态
TERMINATED:线程被销毁之后的状态
TIMED_WAITING:执行了 Thread.sleep(sleepTime) 方法,wait(timeout),join(timeout)
BLOCKED:线程在等待锁的时候的状态
WATING:执行了 wait() ,join() 后线程所处的状态

守护线程

java中线程分为两种,一种是用户线程,另一种是守护线程
守护线程:
守护线程是一种特殊的线程,它的特性有“陪伴”的含义,当进程中不存在非守护线程了,则守护线程自动销毁。典型的守护线程就是垃圾回收线程。Daemon 的作用就是为其他线程的运行提供便利服务。任何一个守护线程都是整个JVM中所有非守护线程的“保姆”,只要当前JVM实例中存在任何一个非守护线程没有结束,守护线程就在工作,只有当最后一个非守护线程结束时,守护线程才随着JVM一同结束工作。

线程池

线程池的出现可以让线程进行复用,而不会因为启动大量线程给系统造成负担;当有任务提交,则从线程池中拿出一个线程去执行任务,执行完又会将线程返还给线程池。

  1. 线程池原理

有多种线程池,一般是调用 Executors 的类方法去创建线程池,我们从 Executors 源码中可以发现,各类线程池都是基于 ThreadPoolExecutor ,传入参数不同形成了功能特性不同的线程池。

所以我们从 ThreadPooExecutor 源码来分析线程池
在这里插入图片描述
ThreadPoolExecutor 的构造方法(其中参数最多的构造方法)如下:

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
    if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}
  • 重要属性
    corePoolSize :核心线程池大小
    maximumPoolSize:线程池允许的最大线程数
    keepAliveTime:线程数大于核心线程数时,处于空闲的线程的存活时间
    timeUnit:时间单位,即 keepAliveTime 的单位
    workQueue:线程池的任务缓存队列,就是一个阻塞队列,当线程数(应该说是提交上来的任务数)大于核心线程数时,新提交的线程(任务)就放到阻塞队列中等待
    threadFactory:线程工厂,用来创建线程
    handler:当提交线程(任务)数大于最大线程数时的丢弃策略

  • 任务缓存队列
    用于缓存等待执行的线程(任务),从 ThreadPoolExecutor 源码可知,其类型是 BlockingQueue< Runnable >,通常有三种类型:
    ArrayBlockingQueue:有限任务缓存队列,基于数组实现的先进先出队列,创建时需要指定大小
    LinkedBlockingQueue :无限任务缓存队列,基于链表实现的先进先出队列,创建时无需指定大小
    SynchronousQueue:无缓冲等待队列,直接提交,并不缓存,提交的任务在队列中,如果未被取出,则后面的任务都无法进入队列。
    PriorityBlockingQuene:有优先级的无界阻塞队列。

  • 丢弃策略
    从 ThreadPoolExecutor 源码可知,该类有四个内部静态类,均为 RejectedExecutionHandler 接口的实现类,四种丢弃策略。

CallerRunsPolicy 类:

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
        r.run();
    }
}

除非线程池关闭,否则都会处理任务。
AbortPolicy类:

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString());
}

丢弃该任务,并抛出异常。
DiscardPolicy类:

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}

直接丢弃,不抛异常。
DiscardOldestPolicy类:

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
        e.getQueue().poll();
        e.execute(r);
    }
}

如果线程池关闭,直接丢弃该任务;
选择队列中最先进入队列等待的任务将其丢弃,并处理该任务。
2. 线程池工作原理
线程池
ThreadPoolExecutor 的 executor 方法的源码如下:

 public void execute(Runnable command) {
     if (command == null)
         throw new NullPointerException();
     int c = ctl.get();
     if (workerCountOf(c) < corePoolSize) {
         if (addWorker(command, true))
             return;
         c = ctl.get();
     }
     if (isRunning(c) && workQueue.offer(command)) {
         int recheck = ctl.get();
         if (! isRunning(recheck) && remove(command))
             reject(command);
         else if (workerCountOf(recheck) == 0)
             addWorker(null, false);
     } 
     else if (!addWorker(command, false))
         reject(command);
}

下面是工作流程:
线程池工作流程
1、线程池刚被创建出来的时候,线程数为0,此时如果提交任务,则新建线程去执行。
2、当目前线程池中的线程数小于核心线程数时,提交任务就新建线程去执行,添加核心线程成功则返回,不成功执行放入阻塞队列等待被执行。
3、当阻塞队列也满了的时候,如果线程数小于最大线程数,则创建新的线程去执行任务。
4、如果线程数大于最大线程数时则将拒绝执行该任务,实行丢弃策略。
3. 各类线程池的工作原理及特性

FixedThreadPool 固定大小线程池,创建出来的线程和线程池同存活

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
    
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory,threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory);
}

从源码中可知,FixedThreadPool 的核心线程数和最大线程数相等,空闲线程存活时间为0,因为线程数达到核心线程数时就执行丢弃策略了。阻塞队列使用的是 LinkedBlockingQueue ,无限队列。

CachedThreadPool 无限大小线程池,所有线程存活时间为 60s

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}

public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory);
}

核心线程数为0,所有线程创建出来,空闲时存活时间为60s,阻塞队列为 SynchronousQueue
SingleThreadPool 单线程池

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()))
}

public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory));
}

核心线程数和最大线程数均为1,线程存活时间为0,如果无任务提交则线程池也关闭,当线程在忙时,新提交的任务就进入到 LinkedBlockingQueue 阻塞队列等待。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值