java创建线程有三种方式,继承Thread类,实现Runnable接口,实现Callable接口;但Callable接口与FutureTask配合使用,而FutureTask也是实现Runnable接口,所以也也可以说两种方式。
创建线程
继承Thread类
继承Thread类重写run()方法,不过一般推荐通过实现Runnable来创建线程,因为只能继承一个类,如果想要继承其他类拓展,继承Thread就不是很友好。
public class ThreadOne extends Thread {
@Override
public void run() {
System.out.println("thread one");
}
}
调用也很简单,只要执行start()方法即可,但如果是执行run()方法,那只是执行普通方法,并不是创建线程执行的。
ThreadOne threadOne = new ThreadOne();
threadOne.start();
实现Runnable接口
public class ThreadTwo implements Runnable {
@Override
public void run() {
System.out.println("thread two");
}
}
创建Runnable线程需要把类传入Thread实例中。
ThreadTwo threadTwo = new ThreadTwo();
Thread thread = new Thread(threadTwo);
thread.start();
实现Callable接口
这种方式创建线程是可以获得子线程返回结果的,是Runnable的加强版。
public class ThreadThree implements Callable<Long> {
@Override
public Long call() throws Exception {
System.out.println("thread three");
return 23L;
}
}
这种方式也需要把实例传入Thread中才能创建线程,执行完之后可以通过FutureTask的get()获取返回结果。
ThreadThree threadThree = new ThreadThree();
FutureTask<Long> future = new FutureTask<>(threadThree);
new Thread(future).start();
//输出返回结果
System.out.println(future.get());
创建线程池
如果在程序中随意创建线程,需要就创建,这是一种不好的方式,不容易管理,代码混乱,同时可能造成一些线程问题。需要用到多线程就必须使用线程池,这是一个好的开发习惯。
线程池主要是实现ExecutorService,而jdk已经实现,可以通过Executors类创建或者new ThreadPoolExecutor来创建,而Executors实际也是通过new ThreadPoolExecutor创建,不过不推荐使用Executors方式创建,至于原因网上很多文章说明,阿里巴巴开发规范也不推荐使用这种方式。
ExecutorService 的submit把要创建的线程提到线程池,由线程池执行,同时可以获取子线程的返回结果。
public class Test {
private static ExecutorService executorService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),
Runtime.getRuntime().availableProcessors() * 2, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(512),
new ThreadPoolExecutor.DiscardPolicy());
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadOne threadOne = new ThreadOne();
ThreadTwo threadTwo = new ThreadTwo();
ThreadThree threadThree = new ThreadThree();
Future<?> a = executorService.submit(threadOne);
Future<?> b = executorService.submit(threadTwo);
Future<?> c = executorService.submit(threadThree);
//输出结果
System.out.println(a.get());
System.out.println(b.get());
System.out.println(c.get());
}
}
在实际中,经常有这样的场景,大量的数据然后分批次建立子线程来执行,获取子线程执行结果然后统一返回,若线程池以下面的方式操作 就是一种错误方式。在循环中,在线程池中执行子线程,然后把结果保存到集合中,然而这种方式没有多线程效果,在循环中等待子线程执行完之后获取到结果,然后再下一次循环再执行一个子线程。
public static void errorMethod() throws ExecutionException, InterruptedException {
List<Long> result = new ArrayList<>();
for (int i = 0; i < 10; i++) {
ThreadThree threadThree = new ThreadThree();
//阻塞等待结果,实际相当于等子线程执行完之后然后再创建子线程,然后再执行,没有多线程的效果
Future<Long> future = executorService.submit(threadThree);
result.add(future.get());
}
}
正确的姿势应该是这样的:
public static void test() throws InterruptedException, ExecutionException {
List<Callable<Long>> callables = new ArrayList<>();
for (int i = 0; i < 10; i++) {
ThreadThree threadThree = new ThreadThree();
callables.add(threadThree);
}
List<Future<Long>> futures = executorService.invokeAll(callables, 10, TimeUnit.SECONDS);
List<Long> result = new ArrayList<>();
for (Future<Long> future : futures) {
result.add(future.get());
}
}
先把要执行的子线程保存集合,然后调用用invokeAll方法全部执行,设置超时时间,若在超时时间内没有执行完所有子线程抛出异常。
网友评论