线程的状态以及状态之间的切换
线程从创建到死亡有七个状态, 分别是初始状态, 准备运行, 运行状态, 阻塞状态, 睡眠状态 (超时等待状态), 等待状态, 死亡状态, 关系如图所示:
线程的创建
实现线程的几种方式:
继承 Thread 类
实现 Runnable 接口
匿名内部类的方式
带返回值的线程
定时器
线程池实现
Lambda 表达式实现
继承 Thread
- // 无名
- public class Demo01 extends Thread {
- @Override
- public void run() {
- System.out.println(getName() + "执行了...");
- }
- public static void main(String[] args) {
- Demo01 d1 = new Demo01();
- Demo01 d2 = new Demo01();
- d1.start();
- d2.start();
- }
- }
- // 有名
- public class Demo01 extends Thread {
- @Override
- public void run() {
- System.out.println(getName() + "执行了...");
- }
- public Demo01(String name) {
- super(name);
- }
- public static void main(String[] args) {
- Demo01 d1 = new Demo01("first thread");
- Demo01 d2 = new Demo01("second thread");
- d1.start();
- d2.start();
- }
- }
实现 Runnable 接口
实现了 runnable 接口的类是作为一个线程任务存在的, 需要将实例化后的对象传入 Thread 中
- public class Demo02 implements Runnable {
- @Override
- public void run() {
- System.out.println("线程启动");
- }
- public static void main(String[] args) {
- Thread t = new Thread(new Demo02());
- t.start();
- }
- }
匿名内部类实现
- // 继承 Thread 类的方式
- public class Demo03 {
- public static void main(String[] args) {
- new Thread() {
- @Override
- public void run() {
- System.out.println("启动线程...");
- }
- }.start();
- }
- }
- // 实现 Runnable 接口的方式
- public class Demo03 {
- public static void main(String[] args) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- System.out.println("线程启动...");
- }
- }).start();
- }
- }
带返回值的线程
上面创建的线程都是不带返回值以及不能抛异常的线程, 接下来实现一种带返回值的线程
- import java.util.concurrent.Callable;
- import java.util.concurrent.ExecutionException;
- import java.util.concurrent.FutureTask;
- public class Demo04 implements Callable<Integer> {
- public static void main(String[] args) throws ExecutionException, InterruptedException {
- Demo04 d4 = new Demo04();
- FutureTask<Integer> task = new FutureTask<>(d4);
- Thread t = new Thread(task);
- t.start();
- System.out.println("计算结果是:" + task.get());
- }
- @Override
- public Integer call() throws Exception {
- System.out.println("正在进行紧张的计算...");
- Thread.sleep(3000);
- return 1;
- }
- }
定时器
定时器 Timer 通过 schedule 方法主席那个定时任务, 可以指定延时多久执行, 每隔多久执行, 甚至是指定一个日期执行; 或者是指定第一次执行的日期, 然后每隔多久执行等等.
- import java.util.Timer;
- import java.util.TimerTask;
- public class Demo05 {
- public static void main(String[] args) {
- Timer timer = new Timer();
- timer.schedule(new TimerTask() {
- @Override
- public void run() {
- // 定时器执行的任务
- System.out.println("定时器正在执行");
- }
- }, 0, 1000); // 延时 0 秒执行, 每隔一秒执行一次
- }
- }
基于线程池实现
线程池用于降低线程创建和销毁的开销, 需要线程时直接从池子里面拿, 不需要时也不会销毁而是放回池子里面等待下一次使用, 典型的拿空间换时间.
Executors 创建
线程池最顶级的接口是 Executor,Executor 里面只有一个 execute 方法, 这个方法用于执行线程任务, 但是线程池在执行完所有的任务后不会停止, 需要执行 shutdown() 方法, 但是这个方法没有在 Executor 中声明, 而是在 ExecutorService 类中实现的, 所以使用 ExecutorService 方法接收线程池对象从而执行 shutdown() 方法关闭线程池
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- public class Demo06 {
- public static void main(String[] args) {
- ExecutorService e = Executors.newFixedThreadPool(10);
- for (int i = 0; i <100; i ++) {
- e.execute(new Runnable() {
- @Override
- public void run() {
- System.out.println(Thread.currentThread().getName());
- }
- });
- }
- e.shutdown();
- }
- }
上面是通过 Executors 静态工厂创建的线程池, Executors 一共提供了五个核心方法, 分别是:
Executors.newWorkStealingPool(): 这个方法在 JDK1.8 中引入, 创建持有足够线程的线程池支持给定的并行度, 并通过使用多个队列减少竞争
Executors.newCachedThreadPool():maximumPoolSize 最大可以到 Integer.MAX_VLUE, 线程数不够会自动增加, 比较弹性. 存在 OOM 异常, 能够回收不工作的线程, keepAliveTime 默认 60, 也就是 60 秒回收一个空闲线程
Executors.newScheduledThreadPool(): 线程数最大至 Integer.MAX_VLUE, 同样存在 OOM 风险, 支持定时及周期性执行任务, 优于 Timer, 不会回收不工作的线程
Executors.newSingleThreadExecutor(): 创建一个单线程的线程池, 相当于单线程串行执行所有任务, 保证按任务的提交顺序依次执行
Executors.newFixedThreadPool(): 输入的参数就是固定的线程数, 既是核心线程数也是最大线程数, 不存在空闲线程, 所以 keepAliveTime 为 0
使用 ThreadPoolExecutor 创建
一般不推荐使用 Executors 创建二十建议使用 ThreadPollExecutor, 因为 Executors 中的方法允许的请求队列长度是 Integer.MAX_VALUE, 可能会导致堆积大量的请求, 从而导致 OOM
ThreadPoolExecutor 的构造方法由 7 个参数:
corePoolSize: 表示核心线程数, 核心线程在任务执行完毕后不会销毁
maximumPoolSize: 线程池能够容纳的最大线程数, 必须大于 1, 如果执行的任务数超过了该值, 多出的任务数会被放入队列中等待执行
keepAliveTime: 表示线程池中允许线程空闲的时间, 当空闲时间达到 keepAliveTime 时该线程就会被销毁, 直到池中只有 corePoolSize 数量的线程为止. 如果想让核心线程超时后也被回收, 只需要将 ThreadPoolExecutor 的 allowCoreThreadTimeOut 变量设置为 true
TimeUnit: 表示 keepAliveTime 的时间单位, 通常时 TimeUnit.SECONDS
workQueue: 缓存队列, 当请求的任务数大于 maxiumPoolSize 后, 超过的任务会进入 BlockingQueue 阻塞队列中
threadFactory: 用于生产一组相同任务的线程, 线程的命名就是通过给这个 factory 增加组名前缀来实现的
handler: 执行拒绝策略的对象, 当执行的任务数超过第五个参数 workQueue 的缓存限制时, 及就可以使用该策略处理请求, 比方说将请求转向某个提示页面表示当前执行任务数过多
上面的队列, 线程工厂, 拒绝处理服务都必须有实例对象, 但是再实际编程中都是通过 Executors 这个线程池静态工厂提供默认的实现, Executors 用法上面已经说明, 那么现在使用 ThreadPoolExecutor 来创建一个线程池
第一步: 实现队列, 线程工厂, 拒绝服务
- // 线程工厂
- class UserThreadFactory implements ThreadFactory {
- private final String namePrefix;
- private final AtomicInteger nextId = new AtomicInteger(1);
- UserThreadFactory(String name) {
- namePrefix = "该线程来自:" + name + "---- 这是该线程池中的第";
- }
- /**
- * 该方法用于创建线程池中的线程
- * @param r
- * @return
- */
- @Override
- public Thread newThread(Runnable r) {
- String name = namePrefix + nextId.getAndIncrement() + "个线程";
- Thread thread = new Thread(null, r, name, 0);
- System.out.println(thread.getName()); // 打印线程名称
- return thread;
- }
- }
- // 拒绝服务实现
- class UserRejectHandler implements RejectedExecutionHandler {
- @Override
- public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
- System.out.println("任务太多啦, 被拒绝了" + executor.toString());
- }
- }
第二步: 实现线程任务
- class Task implements Runnable {
- @Override
- public void run() {
- System.out.println("执行中...");
- }
- }
创建线程池
- public class Demo07 {
- public static void main(String[] args) {
- // 设置了缓存队列的大小为 2
- ThreadPoolExecutor tpe = new ThreadPoolExecutor(1, 2, 60, TimeUnit.SECONDS,
- new LinkedBlockingDeque<>(2), new UserThreadFactory("线程池工厂一"), new UserRejectHandler());
- for (int i = 0; i < 200; i++) {
- tpe.execute(new Task());
- }
- }
- }
Lambda 表达式实现
使用 parallelStream() 实现
来源: http://www.bubuko.com/infodetail-3333272.html