无返回值线程
线程创建
通过继承Thread类或者实现Runnable接口进行创建
class T2 extends Thread {
@Override
public void run() {
System.out.println("T2:" + System.currentTimeMillis());
}
}
class T1 implements Runnable {
@Override
public void run() {
System.out.println("T1:" + System.currentTimeMillis());
}
}
线程启动
必须使用Thread的start方法启动线程。不能直接调用run方法
public static void main(String[] args) throws Exception {
T1 t1 = new T1();
T2 t2 = new T2();
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
thread1.start();
thread2.start();
System.out.println("main:over");
}
有返回值的线程
线程创建
通过实现callable接口创建线程
class T3 implements Callable<String> {
@Override
public String call(){
return "T3:" + System.currentTimeMillis();
}
}
线程启动
1.创建FutureTask类,传入Callable接口
2. 创建Thread类,传入FutureTask
3. 使用Thread的start方法启动线程
4. 使用futureTask类的get方法获取返回值,此时线程处于阻塞状态
public static void main(String[] args) throws Exception {
T3 t3 = new T3();
FutureTask<String> futureTask = new FutureTask<>(t3);
Thread thread3 = new Thread(futureTask);
thread3.start();
String s = futureTask.get();
System.out.println(s);
System.out.println("main:over");
}
获取的结果中,main:over在最后输出,因为futureTask.get()方法阻塞。
线程池
使用java自带的java.util.concurrent包中的工具类进行处理
线程池创建
创建4种类型的线程池,分别是:
- 固定数量线程池:线程数量固定。排队数量LinkedBlockingQueue可以达到Integer的最大值,导致oom
- 单线程线程池:只有一个线程。排队数量同样可以达到Integer的最大值,导致oom
- 动态扩展线程池:线程数量动态更改,可以创建Integer的最大值个线程,导致oom
- 带有最小数量的动态扩展线程池:初始线程固定,随着负载增加动态增加线程数,线程数可达到Integer的最大值,导致oom
ExecutorService executorService = Executors.newFixedThreadPool(5);
ExecutorService executorService1 = Executors.newSingleThreadExecutor();
ExecutorService executorService2 = Executors.newCachedThreadPool();
ExecutorService executorService3 = Executors.newScheduledThreadPool(5);
使用如下代码,可以直接把电脑搞挂掉
public class N4 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
TN1 tn1 = new TN1();
executorService2.execute(tn1);
}
System.out.println(Thread.currentThread().getName());
executorService.shutdown();
}
}
class TN1 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
线程池关闭
使用shutdown方法关闭线程池,如果现称此不关闭,则程序不会结束
executorService.shutdown();
带返回值的线程池调用
class K1 implements Callable<String> {
@Override
public String call() {
return Thread.currentThread().getName();
}
}
public class N4 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(5);
K1 k1 = new K1();
FutureTask<String> futureTask = new FutureTask<>(k1);
executorService.submit(futureTask);
String s = futureTask.get();
System.out.println(s);
System.out.println(Thread.currentThread().getName());
executorService.shutdown();
}
}
跟上面类似,使用FutureTask进行包装,然后通过FutureTask拿到返回值,拿返回值的方法是阻塞方法。
springboot中使用多线程
正常情况下web请求到controller层,再到service层,如果打印线程名称。则会发现,使用的是同一个线程。
开启异步线程首先需要在启动类上添加@EnableAsync,然后在需要异步的方法上添加@Async注解。
@SpringBootApplication
@EnableAsync
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
@RestController
public class HelloController {
@Autowired
private HelloService helloService;
@GetMapping("/say")
public String say() {
System.out.println("controller "+Thread.currentThread().getName());
return helloService.say();
}
}
@Service
public class HelloService {
@Async
public String say() {
System.out.println("service "+Thread.currentThread().getName());
return "say";
}
}
此时从浏览器上访问say方法。1、会发现没有返回值。2、从控制台的输出能看到,controller和service中使用的不是同一条线程
注意:在同一个类中,A方法调用B方法,B方法上有注解@Async,则不会有新线程处理。
结论:如果想要异步调用方法,则必须把异步方法写在单独的类中。在其他类中调用。
springboot中使用有返回的多线程
同上面一样,如果想要有返回值,则需要使用Futrue进行包装
@Async
public Future<String> hi() {
System.out.println("hi " + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
AsyncResult<String> result = new AsyncResult<>("hello");
return result;
}
方法上的注解还是@Async,但是返回值的类型需要时Future。在方法调用时,通过future的get方法获取返回内容。get方法同样是阻塞方法。
public void hello() throws ExecutionException, InterruptedException {
Future<String> hi = hiService.hi();
String s = hi.get();
System.out.println(s);
System.out.println("service "+Thread.currentThread().getName());
}
注意:如果不设置线程池的参数,springboot默认使用了ThreadPoolTaskExecutor做的submit操作。这个线程池默认的maxPoolSize是Integer的最大值,如果不设置,线程量太大的话,会造成oom。
设置的话,可以使用配置文件,重新设置各个参数
@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.initialize();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);
return executor;
}
}
这时就可以使用我们配置的参数进行线程创建