线程简介
实现一个线程,必须创建一个thread实例,override run方法,并且调用start方法启动线程。
线程的生命周期分为new,runnable,running,block,termate.
1. 创建线程的方式
- newThread的方式创建线程
- 实现 Runnable 接口
1.1 new Thread
/**
* 线程的创建、启动
*/
public class ThreadTest {
/**
* 继承 Thread 类 线程测试
*/
@org.junit.Test
public void thread1() {
Thread1 threadTest1 = new Thread1();
Thread1 threadTest2 = new Thread1();
threadTest1.start();
threadTest2.start();
}
}
/**
* 创建线程方式1:继承 Thread 重写 run 方法
* 启动线程的唯一方法就是通过Thread类的start()实例方法
*/
class Thread1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
}
使用new Thread有很多弊端:
- 每次都new,线程缺乏统一管理,相互竞争抢占资源,性能差;
- 缺乏更多功能,如定时执行、定期执行、线程中断;
建议用线程池管理。
1.2 实现 Runnable接口
/**
* 线程的创建、启动
*/
public class ThreadTest {
/**
* 实现 Runnable 接口 线程测试
*/
@org.junit.Test
public void thread2() {
Thread2 threadTest1 = new Thread2();
Thread t1 = new Thread(threadTest1);
Thread2 threadTest2 = new Thread2();
Thread t2 = new Thread(threadTest2);
t1.start();
t2.start();
}
}
/**
* 创建线程方式2:实现 Runnable 接口, 重写 run 方法
* 启动线程的唯一方法就是通过Thread类的start()实例方法
*/
class Thread2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
}
线程池
2. 线程池创建方式
- new ThreadPoolExecutor(…)
- Exceutors.xxx
其中阿里规约强制推荐使用ThreadPoolExecutor的方式!
2.1 ThreadPoolExecutor方式
public static void main(String[] args) throws InterruptedException {
while (true) {
threadDontGcDemo();
Thread.sleep(2000);
}
}
private static void threadDontGcDemo() {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100));
threadPoolExecutor.execute(() -> {
System.out.println("1111");
});
threadPoolExecutor.shutdown();// 不执行shutdown可能会造成内存泄漏
}
2.2 Executors方式
Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待;
Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程;
Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序;
Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池;
Executors.newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池;
Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定)【JDK 1.8 添加】。
常用的是newCachedThreadPool,
package cn.com.gs.demo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author Administator
* @date 2021-10-19 16:04
* @Description
*/
public class ThreadPoolTest {
public static ExecutorService execs = Executors.newCachedThreadPool();
/**
* 线程复用测试
*
* @param args
*/
public static void main(String[] args) {
// 模拟10个任务
for (int i = 0; i < 10; i++) {
// 线程内任务索引
int taskIndex = i;
try {
// 每隔一秒,向线程池添加一个任务,一秒足以让下面输出的任务执行完,
// 任务执行完线程就会空闲,即可实现线程的复用;
// 任务没有执行完,将会再次创建线程
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
execs.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "---->正在执行的任务-->" + taskIndex);
}
});
}
}
}
线程池与SpringBoot整合
我们使用线程池,第一个目的是统一管理线程,当然更希望多线程执行任务对不对,请看下文。
- 使用@EnableScheduling开启定时任务;
- 创建线程池,此处使用Executors.newScheduledThreadPool(可执行延时任务);
- 继承SchedulingConfigurer,重写configureTasks方法,实现多线程;
- 实际使用:@Scheduled标注的方法,执行时都会用线程池里的线程执行;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
}
@Bean(destroyMethod="shutdownNow")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(20); //指定线程池大小
}
}
随笔
记录一下之前研究的修改cron表达式,来修改任务执行时机
import cn.com.infosec.netseal.common.config.ConfigUtil;
import cn.com.infosec.netseal.common.util.DateUtil;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
@EnableScheduling
public class TestSchedule implements SchedulingConfigurer {
private static String cron;
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
scheduledTaskRegistrar.addTriggerTask(new Runnable() {
@Override
public void run() {
System.out.println("定时执行了" + DateUtil.getDateTime());
}
}, new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
try {
cron = ConfigUtil.getInstance().getCron();
CronTrigger cronTrigger = new CronTrigger(cron);
Date date = cronTrigger.nextExecutionTime(triggerContext);
return date;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
});
}
}