Java中有几种方式来创建线程执行任务,分别是什么?
- 1、继承Thread类
public class MyThread extends Thread{public static void main(String[] args) {MyThread myThread = new MyThread();myThread.start();}@Overridepublic void run() {System.out.println("Hello");}
}
总结:重写的是run()方法,而不是start方法,但是占用继承的名额,Java中的类是单继承的。
延伸:java中的接口可以多继承
- 2、实现Runnable接口
public class MyThread extends Thread{public static void main(String[] args) {MyThread myThread = new MyThread();myThread.start();}@Overridepublic void run() {System.out.println("Hello");}
}
总结:实现Runnable接口,实现run()方法,使用依然要用到Thread,此方式更常用
有时候,我们会直接使用匿名内部类的方式或Lambda表达式的方式(如下);
public class MyRunnable2 {public static void main(String[] args) {Thread thread = new Thread(()-> System.out.println("Hello MyRunnable2"));thread.start();}
}
- 3、实现Callable接口
public class MyCallable implements Callable<String> {public static void main(String[] args) throws ExecutionException, InterruptedException {FutureTask<String> futureTask = new FutureTask<>(new MyCallable());Thread thread = new Thread(futureTask);thread.start();String result = futureTask.get();System.out.println(result);}@Overridepublic String call() {return "Hello MyCallable";}
}
总结:实现Callable接口,实现call()方法,得使用Thread+futureTask配合,这种方式支持拿到异步执行的任务结果。
- 4、利用线程池来创建线程
public class MyExecutor implements Runnable {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(10);executorService.execute(new MyExecutor());}@Overridepublic void run() {System.out.println("Hello MyExecutor");}
}
总结:实现Callable接口或者Runnable接口都可以,有ExecutorService来创建线程。
注意:工作中,不建议使用Executors来创建线程池。
总结:
以上集中方式,底层都是基于Runnable。
为什么不建议使用Executors来创建线程池?
1、FixedThreadPool
当我们使用Executors创建FixedThreadPool时,对应的构造方法为:
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());}
发现创建的队列为LinkedBlockingQueue,是一个无界阻塞队列,如果使用该线程池执行任务,任务过多时就会不断的添加到队列中,任务越多占用的内存就越多,最终可能耗尽内存,导致OOM(全称“Out Of Memory”,内存溢出,通俗理解就是内存不够啦)。
2、SingleThreadExecutor
当我们使用Executors创建SingleThreadExecutor时,对应的构造方法为:
public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));}
也是LinkedBlockingQueue,所以同样可能会耗尽内存。
总结:除了可能造成的内存溢出外,我们使用Executors来创建线程池也不能自定义线程的名字,不利于排查问题,所以建议直接使用ThreadPooLExecutor来定义线程池,这样可以灵活控制。
线程池有哪几种状态,每种状态分别表示什么?
1、RUNNING
表示线程池正常运行,既能接受新任务,也会正常处理队列中的任务
2、SHUTDOWN
当调用线程池的shutdown()方法时,线程池就会进入SHUTDOWN状态,表示线程池处于正在关闭状态,此状态下线程池不会接受新任务,但是会继续把队列中的任务处理完。
3、STOP
当调用线程池的shutdownnow()方法时,线程池就会进入STOP状态,表示线程池处于正在停止状态,此状态下线程池既不会接受新任务了,也不会处理队列中的任务,并且正在运行的线程也会被中断。
4、TIDYING
线程次中没有线程在运行后,线程池的状态就会自动变为TIDYING,并且会调用terminated(),该方法是空方法,留给程序员进行扩展。
5、TERMINATED
terminated()方法执行之后,线程池状态就会变为TERMINATED