校招面试准备——Java如何创建线程(Thread runnable callable 线程池)

本文详细介绍了Java中创建线程的四种方式:继承Thread类、实现Runnable接口、使用Callable接口以及利用线程池。讲解了Thread类的重要成员变量和方法,以及Runnable接口的使用场景。还提到了Callable接口与FutureTask的配合使用,以及线程池的工作原理和Java中预定义的四种线程池。
摘要由CSDN通过智能技术生成

Java如何创建线程

1. 继承Thread类

Thread类是Java提供的一个实现了Runnable接口的类。

它主要的成员变量有:

name

priority(优先级,默认会从创建该线程的线程中继承;最小为1,最高为10,默认为5),

daemon(bool值,表示是否为守护线程),

target(runnable类型, 如果是用runnable对象创建的,则该runnable对象为target,调用run方法时会调用target的run(); 如果直接继承Thread类,则target为null),

group(线程组),

contextCassLoader(类加载器)以及threadLocals(是threadLocal表),

tid(线程id),threadStatus(volatile类型,表示线程状态)。

 

静态成员变量:threadSeqNumber      tid是递增的

 

静态方法

currentThread()   用于返回当前正在执行的线程引用

yield()   通知调度器当前线程愿意放弃CPU,调度器可以忽略这个请求

sleep(long millis) throws InterruptedException   使当前线程处于睡眠状态,并且不释放它获得的任何资源

 

成员方法

publlic synchronized void start()  创建一个新的thread对象后,调用该方法做一些准备工作,之后该线程进入runnable状态。准备工作例如:判断该线程的线程状态是否为0 (0代表new),将该线程加入到线程组中。

public void run()  如果target不为null,调用target的run();如果target为null,即直接继承Thread类创建线程,需要重写run(),否则该线程什么也不做

final synchronized join(final long millis)      这个方法的作用是,等待调用这个方法的线程对象执行完再去执行别的线程。例如我们a.join(0)意味着让线程a执行完再执行别的线程。如果millis为0,就一直等待;如果非0,则等待millis长时间

public final synchronized void join(final long millis)
    throws InterruptedException {
        if (millis > 0) {
            if (isAlive()) {    //这里判断的是this是否存活
                final long startTime = System.nanoTime();
                long delay = millis;
                do {
                    wait(delay);    //wait是使调用这个方法的线程(即现在占用cpu的线程)等待
                                    //调用这个方法的线程 不等于 this
                } while (isAlive() && (delay = millis -
                        TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) > 0);
            }
        } else if (millis == 0) {
            while (isAlive()) {
                wait(0);   // 同上边的wait
            }
        } else {
            throw new IllegalArgumentException("timeout value is negative");
        }
    }

2. 令一个类实现Runnable 接口,并使用该类的对象生成一个Thread对象

Runnable接口中仅有一个抽象函数run()。他存在的目的是Java不允许多继承,如果一个类想要在继承了别的类的同时成为一个线程类,就不能继承Thread类,可以通过实现接口,变成一个线程类。

此外,如果有一个类实现了Runnable接口,并且有成员变量,则可以使用该类的某个对象生成多个Thread对象,即多个线程对象共享一个Target,且这些Thread对象共享该类对象的成员变量。

 

3. 通过callable接口

这个接口在util.concurrent包下,而前两种方法都在long包下

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

这个接口只有一个有返回值(返回值类型使用了模板)的方法call() ,该方法可以抛出异常。而Runnable接口中的run()方法没有返回值。Callable接口需要与FutureTask对象(继承了Future和Runnable)一起使用。使用的方法是,首先用一个实现了Callable接口的类的对象创建一个FutureTask对象,之后将FutureTask对象作为Thread对象的Target创建一个Thread对象(因为FutureTask继承了Runable接口)。同时,FutureTask中有一个run()方法,run()方法会调用callable对象的call()方法。

FutureTask中,任务共有6种状态: 这些状态的转换我还没有搞明白...

    private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;

刚刚提到callable的call()有返回值,我们可以直接调用FutureTask对象的get()方法获取这个返回值。

    get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;

    get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。

此外,FutureTask还提供了三种方法,分被用于取消线程、判断线程是否被取消成功,以及线程是否完成

boolean cancel(boolean mayInterruptIfRunning);

  • cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。
  • 参数mayInterruptIfRunning表示是否允许取消 正在执行没有执行完毕 的任务,如果设置true,则表示可以取消正在执行过程中的任务。     
  • 如果任务已经完成,此方法肯定返回false;
  • 如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;
  • 如果任务还没有执行,肯定返回true。

boolean isCancelled();

  • isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。判断status>=cancelled;

boolean isDone();

  • isDone方法表示任务是否已经完成,若任务完成,则返回true;判断status > NEW

 

4. 使用线程池

Java提供了一个线程池接口 Executor(我不确定该接口是否只用于线程池,求各位大佬纠正 指教),它只有一个方法  void execute(Runnable command)

即,Java中的线程池接受Runnable对象作为任务。使用线程池意味着程序员不需要再 些下面的代码来创建线程执行任务:

          new Thread(new RunnableTask()).start()

而只需要创建线程池,将任务(runnable对象)提交给线程池,由它来决定以什么顺序以及让哪个线程来执行任务(执行任务就是调用runnable对象的run()方法)。伪代码如下:

          Executor executor = new Executor();
          executor.execute(new RunnableTask1());
          executor.execute(new RunnableTask2());

接下来介绍Java中与线程池相关的类、接口等

ExecutorService   这也是一个接口,它继承了Executor接口,并增加了更多的方法

AbstractExecutorService  是一个实现了ExecuotrService接口的抽象类,它也是Java中所有线程池都要隐式继承的抽象类

ThreadPoolService  是继承并实现了AbstractExecutorService所有抽象方法的普通类,也是正真代表线程池类的类。在Java中,不论是官方提供的,还是用户自己想创建线程池,只需要创建ThreadPoolService对象就可以了。在ThreadPoolService提供了四种构造函数,构造函数中需要传入的参数有:

corePoolSize  :  线程池中最少线程的数量。当用户提交一个新的任务时,首先判断现在线程池中线程量是否小于corePoolSize,如果是,则创建一个新的线程去执行这个任务;否则判断现在任务队列是否能够成功添加任务,如果能 就将它加入队列;否则判断当前线程数是否>maximunpoolsizes,不大于则新增线程,否则执行拒绝策略;

【指路一个很好的线程池流程的连接:https://www.cnblogs.com/linguanh/p/8000063.html

maximumPoolSize :  线程池中最大线程数量,会根据workqueue类型决定线程池最大线程数

keepAliveTime  :  当空闲的线程数量超过corePoolSize时,多余的线程会在多长时间内被销毁;此外还有一个相关的参数为allowCoreThreadTimeOut,这个参数表示是否允许corePoolSize以内的空闲线程被销毁。比如coolPoolSize为3,现在线程池有5个线程,其中4个为空闲的;如果 allowCoreThreadTimeOut == true,则所有的空闲线程都会被回收;否则只会回收2个空闲线程,留下3个(即使这三个中有两个是空闲的)

unit :  keepAliveTime的单位

workQueue (BlockingQueue)  :  任务队列(假设我们的线程池中只有3个线程,按现在用户提交了5个任务,因此需要有2个任务先被存下来,等执行完前三个再执行他们,因此要将这2个线程存下来)任务队列的类型换一篇帖子记录吧,今天学不动了。

threadFactory  :  线程工厂,用它来创建线程池中的线程而不是 new thread()

handler (RejectedExecutionHandler) : 拒绝策略,如果任务队列已经满了 而且线程池中的线程数也到达了最大,但是又来了新的任务,那要怎么处理这些任务呢?有四种已经实现好的策略:当然用户也可以自己实现拒绝策略,通过构造函数应用它

  • abort(默认的策略。丢弃且抛异常RejectedExecutionException),
  • discard(丢弃但无异常),
  • discardOldest(丢弃最老的未执行的任务,然后把新的放在队列里),
  • callerruns(由提交这个任务的线程处理该任务)

之后,ThreadPoolService中有一个很重要的属性,叫ctl,这个32位变量的高3位代表了线程池的状态(runState),后29位代表了线程池中的有效的线程的数量(workerCount)。

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));   默认的状态是running  workerCount是0

线程池状态共有五种:

  • RUNNING:  Accept new tasks and process queued tasks   很正常
  • SHUTDOWN: Don't accept new tasks, but process queued tasks   不接受新任务,但是队列里的任务还会做
  • STOP:  Don't accept new tasks, don't process queued tasks,and interrupt in-progress tasks

       不接受新任务,不处理队列里的任务,并且中断正在进行的任务

  • TIDYING:  All tasks have terminated, workerCount is zero, the thread transitioning to state TIDYING will run the terminated() hook method  所有的任务都结束了,workerCount为0, 处于TIDYING状态的线程将调用terminated()
  • TERMINATED: terminated() has completed     所有线程的terminated()方法都被执行完了

workerCount:The workerCount is the number of workers that have been permitted to start and not permitted to stop.  The value may be  transiently different from the actual number of live threads, for example when a ThreadFactory fails to create a thread when asked, and when exiting threads are still performing bookkeeping before terminating. The user-visible pool size is reported as the current size of the workers set.【workerCount是已被允许启动但不允许停止的线程数。该值可能与活动线程的实际数量暂时不同,例如,当ThreadFactory在被要求创建线程却无法创建线程时,以及正在退出的线程在终止之前 仍在执行簿记操作时,该值会有所不同。用户可见的池大小报告为工作集的当前大小。】

此外,Java提供了四种线程池,这四种线程池实际上就是创建了四种不同的ThreadPoolService对象(通过构造函数传入不同的参数),而并不是继承ThreadPoolService四种不同的子类(我一开始就是这么以为的)。如果用户想要用官方提供的这四种线程池,Java提供了一个名为 Exexutors(比最开始那个接口多了一个s)类,这个类有很多的静态函数,通过调用不同的静态函数,用户就可以得到想要的线程池对象。有一种周期调度线线程池,不太了解。

创建一个单例线程池的方法,以及方法的实现

// 这是Excutors类的一个静态函数,用户可以这样调用它  Excutors.newSiggleThreadExcutor()得到一个单例线程池
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
        //可以看出,官方也是通过给ThreadPoolExecutor的构造函数传入不同的参数,来生成不同的线程池对象的
        (new ThreadPoolExecutor(1,   //核心线程数为1
                                1,   //最大线程数也为1,因此当任务数超过1时,一定会入队列或者被拒绝
                                0L,  //线程数不会超过1,因此该时间没意义,不会有线程被销毁
                                TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

 

缓冲线程池,特点:线程池无限大  

【官方适用场景】These pools will typically improve the performance of programs that execute many short-lived asynchronous tasks. (这些池通常将提高执行许多短期异步任务的程序的性能。【否则线程过多 线程池过大】)

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE, //一个无限制创建线程的线程池
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>() 
        //SynchronousQueue这是一个直接提交的队列,意味着每个新任务都会有线程来执行
        //如果线程池有可用线程则执行任务,没有的话就创建一个来执行
        );
    }

固定线程池:core和max数量相同,因此线程的数量要么小于core, 要么等于core 

    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, 
                                      nThreads,  //core和max相同,所以是fixed固定的,一旦多余的任务被提交,一定如队列或者被拒绝
                                      0L, //线程数不会超过core,因此如果allowCoreThreadTimeOut==false空闲时间没意义 
                                       //如果是true,则一有空闲线程就被销毁
                                      TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值