创建线程的三种方式

首先看一下java中创建线程的三种方式:
继承Thread类:

class MyThread extends Thread {
    public void run() {
        System.out.println("我使用继承Thread类的方式创建多线程!");
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
}

实现Runnable接口:

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("我使用了实现Runnable接口的方式创建多线程!");
    }
}

public class Main {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        new Thread(myRunnable).start();
    }
}

实现Callable接口:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

class MyCallable implements Callable {
    @Override
    public String call() throws Exception {
        return "我使用的实现Callable接口的方式创建多线程!";
    }
}

public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask futureTask = new FutureTask(new MyCallable());
        new Thread(futureTask).start();
        System.out.println(futureTask.get());
    }
}
创建线程池

线程池相比于单独的线程带来的好处这里就不赘述了。在创建线程池的时候我们一般使用的是Executors这个线程池的工具类。它里面提供了多种类型的线程池:

//创建固定大小的线程池
ExecutorService e1 = Executors.newFixedThreadPool(3);
//创建只有单独一个线程的线程池
ExecutorService e2 = Executors.newSingleThreadExecutor();
//创建一个大小可以扩大的线程池
ExecutorService e3 = Executors.newCachedThreadPool();

以下是与线程池有关的类和接口的继承关系:

java单线程池设置任务超时时间 java线程池定时执行任务_java


再来看一下一个线程池创建的时候会有哪些参数:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

这是一种线程池的方法,其中的一些参数:

corePoolSize:核心线程数
 maximumPoolSize:最大线程数
 keepAliveTime:线程存活时间
 unit:拒绝策略
 workQueue:阻塞队列

使用线程池执行任务的方法:

e1.execute(myThread);

传入的参数是一个线程,因此线程池中的线程执行任务的方法就是execute()方法。这个方法在线程池的顶级接口Executor中就已经存在。在ThreadPoolExecutor类中对他进行了实现。因此我们重点就是对execute()函数进行研究。

以下是execute()方法的源码:

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        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);
    }

我们会发现,方法首先执行了几个ifelse的判断语句。这也就是一个请求在进入线程池执行时候会遇到的四种状态:
1.如果此时核心线程还有空闲,则直接将当前任务加入到核心线程中进行之星;
2.如果此时核心线程已经满了,且阻塞队列未满,则将人物加入到阻塞队列中,阻塞队列会在核心线程空闲时,将队头的任务加入到核心线程中执行;
3.如果此时核心线程和阻塞队列都满了,那么就开辟一个非核心线程执行任务;
4.如果此时非核心线程开辟失败,即非核心线程也满了,那么就执行拒绝策略。

我们通过观察会发现这几种情况下都会执行一个addWorker()方法,这个方法传入的参数是当前的一个线程还有一个bool值,bool值是进行核心区与非核心区的选择。线程值传入到这个方法后会在方法里调用线程的start()方法,即启动线程。而当线程任务执行结束之后,会走到一个退出的方法,但是在这个退出方法的最后,又会调用addWorker()方法,这也就是线程池中的线程能够被循环使用的原因。

接下来来看看java中的四中拒绝策略:

AbortPolicy(默认):拒绝并抛出异常;
 CallerRunsPolicy:使用调用者线程来运行被拒绝的任务,即该任务不会进入线程池;
 DiscardOledestPolicy:丢弃阻塞队列中最老的任务,然后再次尝试提交新任务;
 DiscardPolicy:丢弃不能进入线程池的任务。