java多线程

首页

无返回值线程

线程创建

通过继承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种类型的线程池,分别是:

  1. 固定数量线程池:线程数量固定。排队数量LinkedBlockingQueue可以达到Integer的最大值,导致oom
  2. 单线程线程池:只有一个线程。排队数量同样可以达到Integer的最大值,导致oom
  3. 动态扩展线程池:线程数量动态更改,可以创建Integer的最大值个线程,导致oom
  4. 带有最小数量的动态扩展线程池:初始线程固定,随着负载增加动态增加线程数,线程数可达到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;
    }

}

这时就可以使用我们配置的参数进行线程创建


版权声明:本文为weixin_38400347原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。