创建线程的三种方式
首先看一下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();以下是与线程池有关的类和接口的继承关系:

再来看一下一个线程池创建的时候会有哪些参数:
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:丢弃不能进入线程池的任务。
















