Java 线程创建的四种方式
所有测试用例使用的是 jdk8 ,版本不同可能会有所不同,建议自己亲手试试。
Thread 方式
通过继承Thread类来创建新的线程(不推荐)。示例如下:
创建类
public class ThreadDemo extends Thread {
private Logger logger = LoggerFactory.getLogger(ThreadDemo.class);
@Override
public void run() {
logger.info("Thread方式创建线程");
}
}
测试方法
@Test
public void testThread(){
new ThreadDemo().start();
}
注意:
如果一个继承Thread的类的实例对象,调用两次start()方法,会报异常。
示例:
@Test
public void testThread(){
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();
threadDemo.start();
}
报错:
java.lang.IllegalThreadStateException
在线程创建之后,java 底层会记录线程的状态,第一次调用就会改变状态,再次调用就会报异常
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
Runnable 方式
通过实现Runnable接口来创建线程。
示例如下:
创建类:
public class RunnableEntity implements Runnable {
private Logger logger = LoggerFactory.getLogger(RunnableEntity.class);
@Override
public void run() {
logger.info("Runnable方式创建线程");
}
}
测试方法:
@Test
public void testRunnable(){
new Thread(new RunnableEntity()).start();
}
使用匿名内部类创建接口:
测试方法:
@Test
public void testRunnableAnonymous (){
new Thread(new Runnable() {
@Override
public void run() {
logger.info("Runnable方式创建线程,使用匿名内部类");
}
}).start();
}
Thread和Runnable区别:
- Thread 是类,需要继承,但是一个类只能继承一个类,一旦继承 Thread 之后,无法在继承其他的类。
- Runnable 是接口,一个类可以实现多个接口,所以实现Runnable接口可拓展性更好
- Thread是Runnable接口的实现类
Callable 方式
通过实现Callable接口来创建线程。
创建类:
public class CallableEntity implements Callable<String> {
private Logger logger = LoggerFactory.getLogger(CallableEntity.class);
@Override
public String call() throws Exception {
return "Callable方式创建线程,这个可以带返回值,撒浪嘿呦";
}
}
测试方法:
@Test
public void testCallable () throws ExecutionException, InterruptedException {
FutureTask<String> stringFutureTask = new FutureTask<>(new CallableEntity());
new Thread(stringFutureTask).start();
logger.info("返回值信息:{}", stringFutureTask.get());
}
匿名内部类:
@Test
public void testCallableAnonymous () throws ExecutionException, InterruptedException {
FutureTask<String> stringFutureTask = new FutureTask<>(new Callable<String>() {
@Override
public String call() throws Exception {
return "Callable方式创建线程,匿名内部类";
}
});
new Thread(stringFutureTask).start();
logger.info("返回值信息:{}", stringFutureTask.get());
}
Runnable和Callable的区别
- Runnable没有返回值。
- Callable可以返回值。
线程池方式
自定义线程池(推荐),从中获取线程。
@Test
public void testExecutor () throws ExecutionException, InterruptedException {
ExecutorService executorService = new ThreadPoolExecutor(5, 9, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<>(2), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 0; i < 5; i++){
executorService.execute(new Runnable() {
@Override
public void run() {
logger.info("当前线程名称:{}", Thread.currentThread().getName());
}
});
}
}
参数说明,只是大致表明,后续会补链接,大家也可以自行搜索。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler
) {
使用Executor接口对应的工具类Executors(不推荐):
FixedThreadPool:可定长线程池。
@Test
public void testExecutorsFix () throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++){
executorService.submit(new Runnable() {
@Override
public void run() {
logger.info("FixedThreadPool,当前线程:{}", Thread.currentThread().getName());
}
}).get();
}
}
Executors还有其他的线程池,可以自己尝试。
注意:
使用Executors工具类创建的线程池,FixedThreadPool、SingleThreadPool、CachedThreadPool默认阻塞队列是Integer.MAX_VALUE,不推荐使用。推荐使用自定义线程池,ThreadPoolExecutor,选择合适的阻塞队列的长度。
CompletionService方式 用来提升线程带返回值的效率。
- 使用 Executors工具类 创建的线程池和 ThreadPoolExecutor 创建的线程池,如果需要获取线程的返回值,获取线程的返回值是顺序的,导致有些线程已经执行完,但是正在执行的线程还没获取返回值,则会进行等待,所以效率提升不是很高。
- 使用CompletionService方式,则是哪个线程先执行完之后,哪个线程先获取数据。
代码示例:
@Test
public void testExecutorCompletionCallable () throws InterruptedException, ExecutionException {
CompletionService<String> completionService = new ExecutorCompletionService<>(Executors.newFixedThreadPool(5));
for (int i = 0; i < 5; i++){
int finalI = i;
completionService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
logger.info("第几次循环:{}", finalI);
return "返回结果:" + finalI;
}
});
}
for (int j = 0; j < 5; j++){
logger.info("get:{}", completionService.take().get());
}
}
|