Java多线程创建方式和使用记录
前言
项目中涉及一些线程使用的改造,在使用修改线程的过程中,踩了不少坑.在此归纳总结一下,方便日后避开这些坑.
java使用线程的方法
1.继承Thread类
2.实现Runnable接口
3.使用ExecutorService、Callable、Future实现
其中 1和 2两种方式创建的线程 执行结束都没有返回值,
执行 run方法不可以抛出异常
3是创建线程 有返回值、可以抛出异常.
1.继承Thread类
代码示例:
package com.fl.thread;
public class TestThread extends Thread{
public void run(){
super.run();
System.out.println("子线程执行了--------");
}
}
public class Test {
public static void main(String[] args) {
TestThread testThread = new TestThread();
testThread.start();
System.out.println("主线程执行----------");
}
}
这种线程的使用方式 本质上其实 也是实现Runnable接口的
一个实例,它代表一个线程的实例,启动线程的唯一方法是
通过Thread 类的start 方法,start 方法时一个原生方法,
它会启动一个新线程,并且执行run()方法. 这种的局限性是 使用继承 Thread 类的方式创建线程时,是不支持多继承.
为了实现多继承要实现Runnable 接口.
执行的结果:
注意: 在使用多线程时,运行结果与调用顺序是无关的(这里的结果不代表线程的执行顺序,线程是并发执行的,如果多运行几次,打印顺序可能会不一样。多线程的运行过程中,CPU是以不确定的方式去执行线程的,故运行结果与代码的执行顺序或者调用顺序无关)。
调用run()方法 知识普通的方法调用(main方法中应该调用的是myThread的start方法,而不是run()方法。调用start()方法是告诉CPU此线程已经准备就绪可以执行,进而系统有时间就会来执行其run()方法。而直接调用run()方法,则不是异步执行,而是等同于调用函数般按顺序同步执行,这就失去了多线程的意义了。
),不会启动线程.如果多次
调用 start 方法,会抛出 IllegalThreadStateException
异常.
2.实现Runnable 接口
这种方法用的比较多,就是把继承Thread类改为实现Runnable接口.
代码示例:
package com.fl.thread;
public class MyRunnable implements Runnable{
public void run() {
System.out.println("执行子线程------");
}
}
//测试
public class TestRunnable {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
System.out.println("主线程运行结束--------");
}
}
运行结果:
这里我们可看出 main中可以看到真正创建新线程还是通过Thread创建:
Thread thread = new Thread(runnable);
这一步Thread类的作用就是把run()方法包装成线程执行体,然后依然通过start去告诉系统这个线程已经准备好了可以安排执行。
3.使用ExecutorService、Callable、Future实现
ExecutorService、Callable、Future这个对象实际上都是属于Executor框架中的功能类. 要了解这框架 大家可以看下并发编程 的书籍.
实现Callable接口,重写call()方法,然后包装成java.util.concurrent.FutureTask, 再然后包装成Thread.
Callable:有返回值的线程,能取消线程,可以判断线程是否执行完毕.
代码示例:
package com.fl.thread;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
public Integer call() throws Exception {
System.out.println("获取当前线程的名称"+Thread.currentThread().getName()+
"当前线程的Id"+Thread.currentThread().getName());
int count = 0;
for (int i = 0; i <= 200000; i++) {
count += i;
}
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId()+"end---");
return count;
}
}
//测试
public class TestMyCallable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 将Callable包装成FutureTask,FutureTask也是一种Runnable
MyCallable callable = new MyCallable();
FutureTask<Integer> futureTask = new FutureTask<Integer>(callable);
new Thread(futureTask).start();
// get方法会阻塞调用的线程
Integer sum = futureTask.get();
System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId() + "=" + sum);
}
}
Callable它也是一种函数式接口:
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
上边测试中用到了 FutureTask
它的用法
public class FutureTask<V> implements RunnableFuture<V> {
// 构造函数
public FutureTask(Callable<V> callable);
// 取消线程
public boolean cancel(boolean mayInterruptIfRunning);
// 判断线程
public boolean isDone();
// 获取线程执行结果
public V get() throws InterruptedException, ExecutionException;
}
FutureTask 实现 RunnableFuture
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
比较 总结一下 这三种方式
Thread: 继承方式, 不建议使用, 因为Java是单继承的,继承了Thread就没办法继承其它类了,不够灵活
Runnable: 实现接口,比Thread类更加灵活,
没有单继承的限制
Callable: Thread和Runnable都是重写的run()方法
并且没有返回值,Callable是重写的call()方法并且
有返回值并可以借助FutureTask类来判断线程是否已经
执行完毕或者取消线程执行
当线程不需要返回值时使用Runnable,需要返回值时就使用Callable,一般情况下不直接把线程体代码放到
Thread类中,一般通过Thread类来启动线程
Thread类是实现Runnable,Callable封装成FutureTask,FutureTask实现RunnableFuture,
RunnableFuture继承Runnable,所以Callable
也算是一种Runnable,
所以三种实现方式本质上都是Runnable实现
关系 ExecutorService 用法
ExecutorService是Java提供的线程池,也就是说,每次我们需要使用线程的时候,可以通过ExecutorService获得线程。它可以有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞,同时提供定时执行、定期执行、单线程、并发数控制等功能,也不用使用TimerTask了。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
所有的线程池 最终都是通过这方法来创建的.
详细的可见
https://blog.csdn.net/weixin_43975771/article/details/108034443
推荐阅读
-
Java多线程Queue、BlockingQueue和使用BlockingQueue实现生产消费者模型方法解析
-
Java使用Thread创建多线程并启动操作示例
-
Java多线程中线程的两种创建方式及比较代码示例
-
Java多线程优化方法及使用方式
-
微信小程序授权 获取用户的openid和session_key【后端使用java语言编写】,我写的是get方式,目的是测试能否获取到微信服务器中的数据,后期我会写上post请求方式。
-
Java多线程优化方法及使用方式
-
window下如何使用文本编辑器(如记事本)创建、编译和执行Java程序
-
使用JavaMail创建邮件和发送邮件 Java综合
-
荐 Java中多线程的使用(超级超级详细)线程安全+线程锁原理解析+保证线程安全的三种方式 (同步代码块+同步方法+lock锁) 5
-
Java多线程创建方式和线程池的使用方法