文章目录
- 1. 继承Thread类
- 2. 实现Runnable接口
- 3. 实现Callable接口
- 4. 线程池
- 4.1 利用Executors工具类来创建线程池
- 4.2 为什么不建议使用Executors来创建线程池?
- 4.3 ThreadPoolExecutor是线程池的核心实现类,可以利用它来创建线程池
- 4.4 线程池的状态
可以认为有四种方式,也可以认为有一种,因为都跟Runnable接口有关
1. 继承Thread类
代码
public class Thread1ExtendsThread extends Thread {
// public Thread1(String name) {
// super(name);
// }
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("这里是自定义线程:" + Thread.currentThread().getName() + ", i = " + i + ";");
}
}
public static void main(String[] args) {
// new Thread1("xinliushijian").start();
new Thread1ExtendsThread().start();
for (int i = 0; i < 3; i++) {
System.out.println("这里是main线程:" + Thread.currentThread().getName() + ", i = " + i + ";");
}
}
}
打印
这里是main线程:main, i = 0;
这里是main线程:main, i = 1;
这里是main线程:main, i = 2;
这里是自定义线程:Thread-0, i = 0;
这里是自定义线程:Thread-0, i = 1;
这里是自定义线程:Thread-0, i = 2;
知识点
- new Thread 构造器的参数可以是Runnable,但没有Callable,因为如果是实现了Callable接口的线程,参数应该是FutureTask
- Thread 是Runnable接口的实现类
- 这种方式的坏处是不能再继承其他类了,因为java是单继承
- 没有返回值
2. 实现Runnable接口
代码
public class Thread2Runnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("这里是自定义线程:" + Thread.currentThread().getName() + ", i = " + i + ";");
}
}
public static void main(String[] args) {
Thread2Runnable thread2 = new Thread2Runnable();
new Thread(thread2).start();
for (int i = 0; i < 3; i++) {
System.out.println("这里是main线程:" + Thread.currentThread().getName() + ", i = " + i + ";");
}
}
}
打印
这里是main线程:main, i = 0;
这里是main线程:main, i = 1;
这里是main线程:main, i = 2;
这里是自定义线程:Thread-0, i = 0;
这里是自定义线程:Thread-0, i = 1;
这里是自定义线程:Thread-0, i = 2;
知识点
- 没有返回值
3. 实现Callable接口
代码
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class Thread3Callable implements Callable {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 3; i++) {
sum += i;
System.out.println("这里是自定义线程:" + Thread.currentThread().getName() + ", i = " + i + ";");
}
return sum;
}
public static void main(String[] args) {
// 使用Lambda表达式创建Callable对象, FutureTask类来包装Callable对象
// FutureTask<Integer> future = new FutureTask<>(
// () -> 3
// );
FutureTask<Integer> future = new FutureTask<>(new Thread3Callable());
// 实质上还是以Callable对象来创建并启动线程
new Thread(future).start();
try {
// get()方法会阻塞,直到子线程执行结束才返回
System.out.println("自定义线程返回值:" + future.get());
} catch (Exception e) {
e.printStackTrace();
}
for (int i = 0; i < 3; i++) {
System.out.println("这里是main线程:" + Thread.currentThread().getName() + ", i = " + i + ";");
}
}
}
打印
这里是自定义线程:Thread-0, i = 0;
这里是自定义线程:Thread-0, i = 1;
这里是自定义线程:Thread-0, i = 2;
自定义线程返回值:3
这里是main线程:main, i = 0;
这里是main线程:main, i = 1;
这里是main线程:main, i = 2;
知识点
- 有返回值,需要跟FutureTask搭配使用,来获得线程的执行结果
- FutureTask也是实现了Runnable接口
- 以上三种方式都是new Thread().start()的方式去启动线程
4. 线程池
4.1 利用Executors工具类来创建线程池
代码
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
public class ThreadDemo3Executors {
public static void main(String[] args) {
// 创建线程工厂
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("Async-pool-%d").build();
// 创建定时任务的线程池
Executors.newScheduledThreadPool(4, threadFactory);
// 创建可缓存的线程池,只会重用空闲可用的线程,没有可用的线程时会创建新线程
Executors.newCachedThreadPool();
// 单个线程的线程池
Executors.newSingleThreadExecutor();
// 固定线程数量的线程池
Executors.newFixedThreadPool(3);
}
}
4.2 为什么不建议使用Executors来创建线程池?
以Executors.newFixedThreadPool(3)来举例
源码
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
知识点
我们发现其实他也是利用ThreadPoolExecutor来创建管理线程池,这种方式不是可以的吗!
问题是它没有定制化,
它只能控制核心线程数和最大线程数两个值,且它俩是相等的,其他的都是默认的。
阻塞队列是LinkedBlockingQueue,容量直接Integer最大值,其中可放21亿多的任务,若是真的任务巨多,会造成OOM
4.3 ThreadPoolExecutor是线程池的核心实现类,可以利用它来创建线程池
Executor线程池相关顶级接口,它将任务的提交与任务的执行分离开来
ExecutorService继承并扩展了Executor接口,提供了Runnable、FutureTask等主要线程实现接口扩展
ThreadPoolExecutor是线程池的核心实现类,用来执行被提交的任务
ScheduledExecutorService继承ExecutorService接口,并定义延迟或定期执行的方法
ScheduledThreadPoolExecutor继承ThreadPoolExecutor并实现了ScheduledExecutorService接口,是延时执行类任务的主要实现
源码
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
ThreadPoolExecutor包含了7个核心参数,参数含义:
corePoolSize:核心线程数
maximumPoolSize:最大线程数
keepAliveTime:当线程池中线程数大于corePoolSize,并且没有可执行任务时大于corePoolSize那部分线程的存活时间
unit:keepAliveTime的时间单位
workQueue:用来暂时保存任务的工作队列
threadFactory:线程工厂提供线程的创建方式,默认使用Executors.defaultThreadFactory()
handler:当线程池所处理的任务数超过其承载容量或关闭后继续有任务提交时,所调用的拒绝策略
代码
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadDemo4ThreadPoolExecutor {
public static final ThreadPoolExecutor EXECUTOR_SERVICE;
static {
int corePoolSize = 500;
int maxPoolSize = 500;
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("xinliushijian-thread-pool-%d").build();
RejectedExecutionHandler rejectedExecutionHandler = new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if (!executor.isShutdown()) {
try {
executor.getQueue().put(r);
} catch (InterruptedException e) {
System.out.println("hahaha");
}
}
}
};
EXECUTOR_SERVICE = new ThreadPoolExecutor(corePoolSize, maxPoolSize, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue(), threadFactory, rejectedExecutionHandler);
}
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
List<String> nameList = new ArrayList<>();
List<String> nameResultList = new ArrayList<>();
nameList.add("xiaohua");
nameList.add("xiaohua1");
nameList.add("xiaohua2");
nameList.add("xiaohua3");
nameList.add("xiaohua4");
nameList.add("xiaohua5");
nameList.add("xiaohua6");
nameList.add("xiaohua7");
CountDownLatch countDownLatch = new CountDownLatch(nameList.size());
nameList.forEach(name -> {
dealWithName(name, countDownLatch, nameResultList);
});
System.out.println("nameResultList前: " + nameResultList);
try {
countDownLatch.await();
} catch (InterruptedException e) {
System.out.println("exception");
}
System.out.println("nameResultList后: " + nameResultList);
}
private static void dealWithName(String name, CountDownLatch countDownLatch, List<String> nameResultList) {
EXECUTOR_SERVICE.execute(() -> {
countDownLatch.countDown();
System.out.println(name + "haha");
System.out.println(Thread.currentThread().getName());
// 打印当前线程数
System.out.println("打印当前线程数: " + EXECUTOR_SERVICE.getPoolSize());
// 打印执行任务的线程数
System.out.println("打印执行任务的线程数: " + EXECUTOR_SERVICE.getActiveCount());
// 总任务数 = 正在执行的任务数 + 队列任务数
System.out.println("总任务数 = 正在执行的任务数 + 队列任务数: " + EXECUTOR_SERVICE.getTaskCount());
nameResultList.add(name);
});
}
}
打印
main
xiaohuahaha
xinliushijian-thread-pool-0
xiaohua1haha
xinliushijian-thread-pool-1
打印当前线程数: 3
xiaohua3haha
xinliushijian-thread-pool-3
xiaohua2haha
xinliushijian-thread-pool-2
打印当前线程数: 5
打印当前线程数: 4
打印执行任务的线程数: 6
打印当前线程数: 5
总任务数 = 正在执行的任务数 + 队列任务数: 6
xiaohua5haha
xinliushijian-thread-pool-5
打印当前线程数: 7
打印执行任务的线程数: 6
总任务数 = 正在执行的任务数 + 队列任务数: 8
xiaohua6haha
xinliushijian-thread-pool-6
打印当前线程数: 8
打印执行任务的线程数: 6
总任务数 = 正在执行的任务数 + 队列任务数: 8
打印执行任务的线程数: 5
xiaohua7haha
xinliushijian-thread-pool-7
打印当前线程数: 8
nameResultList前: [xiaohua2, xiaohua5]
打印执行任务的线程数: 6
总任务数 = 正在执行的任务数 + 队列任务数: 8
打印执行任务的线程数: 6
打印执行任务的线程数: 5
总任务数 = 正在执行的任务数 + 队列任务数: 8
总任务数 = 正在执行的任务数 + 队列任务数: 8
总任务数 = 正在执行的任务数 + 队列任务数: 8
xiaohua4haha
xinliushijian-thread-pool-4
nameResultList后: [xiaohua2, xiaohua5, xiaohua6, xiaohua3, xiaohua, xiaohua7, xiaohua1]
打印当前线程数: 8
打印执行任务的线程数: 1
总任务数 = 正在执行的任务数 + 队列任务数: 8
源码
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("xinliushijian-thread-pool-%d").build();
public ThreadFactory build() {
return doBuild(this);
}
private static ThreadFactory doBuild(ThreadFactoryBuilder builder) {
final String nameFormat = builder.nameFormat;
final Boolean daemon = builder.daemon;
final Integer priority = builder.priority;
final Thread.UncaughtExceptionHandler uncaughtExceptionHandler = builder.uncaughtExceptionHandler;
final ThreadFactory backingThreadFactory = builder.backingThreadFactory != null ? builder.backingThreadFactory : Executors.defaultThreadFactory();
final AtomicLong count = nameFormat != null ? new AtomicLong(0L) : null;
return new ThreadFactory() {
public Thread newThread(Runnable runnable) {
Thread thread = backingThreadFactory.newThread(runnable);
Objects.requireNonNull(thread);
if (nameFormat != null) {
thread.setName(ThreadFactoryBuilder.format(nameFormat, ((AtomicLong)Objects.requireNonNull(count)).getAndIncrement()));
}
if (daemon != null) {
thread.setDaemon(daemon);
}
if (priority != null) {
thread.setPriority(priority);
}
if (uncaughtExceptionHandler != null) {
thread.setUncaughtExceptionHandler(uncaughtExceptionHandler);
}
return thread;
}
};
}
public interface ThreadFactory {
/**
* Constructs a new {@code Thread}. Implementations may also initialize
* priority, name, daemon status, {@code ThreadGroup}, etc.
*
* @param r a runnable to be executed by new thread instance
* @return constructed thread, or {@code null} if the request to
* create a thread is rejected
*/
Thread newThread(Runnable r);
}
知识点
从上面源码可以清楚看到,其中创建线程的步骤在ThreadFactory,线程工厂创建的线程是实现了Runnable接口,所以利用线程池来创建线程也是跟Runnable有关,所以这四种创建线程的方式其实都跟Runnable有关。
4.4 线程池的状态
线程池的5种状态
private static final int COUNT_BITS = Integer.SIZE - 3;
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
-
1. RUNNING 运行状态
线程池新建或调用execute()方法后,处于运行状态,能够接收新的任务 -
2. SHUTDOWN 关闭状态
调用shutdown()方法后的状态,此时线程池不再接收新的任务,但会继续处理已提交的任务队列中的任务 -
3. STOP 停止状态
调用shutdownNow()方法后的状态,此时线程池不再接收新的任务,不处理任务队列中的任务,并且中断正在进行的任务 -
4. TIDYING 整理状态
只是一个中间状态,不做任务处理
final void tryTerminate() {
for (;;) {
int c = ctl.get();
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated();
} finally {
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
/**
* Method invoked when the Executor has terminated. Default
* implementation does nothing. Note: To properly nest multiple
* overridings, subclasses should generally invoke
* {@code super.terminated} within this method.
*/
protected void terminated() { }
从上面源码可以看到,此时所有任务都已终止,workerCount为0。
当线程池处于调整状态后执行了一个terminated()方法,而它是个空方法,所以调整状态默认是不会做任务动作的。
通过注释我们发现设计它的用处,它是一个可以被子类继承的protected修饰的方法,我们可以认为调整状态的作用就是方便子类继承扩展的,在任务都终止后做我们自定义的动作。
- 5. TERMINATED 终止状态
线程池内所有的线程都已终止时,就会进入终止状态