Java多线程实现方式主要有四种:
1.继承Thread类 (没有返回值)
2.实现Runnable接口 (没有返回值)
3.实现Callable接口通过FutureTask包装器来创建Thread线程 (有返回值)
4.使用ExecutorService实现有返回结果的多线程。 (有返回值)
1.继承Thread类创建线程
Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程唯一的方法就是通过Thread类的start()实例方法。start()方法是一个native方法(native方法是用来调用计算机内部或其他语言的方法),它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。
例如:
public class User extends Thread{
private int i;
public User(int i) {
this.i = i;
}
@Override
public void run() {
System.out.println("User.run("+i+")");
}
public static void main(String[] args) {
for(int i=1;i<100;i++){
User user = new User(i);
user.start();
}
}
}
2.实现Runnable接口创建线程
如果自己的类已经extends另一个类,因为java中的类是单继承,多实现的,无法直接extends Thread,此时,就可以实现一个Runnable接口,如下:
public class User implements Runnable{
private int i;
public User(int i) {
this.i = i;
}
@Override
public void run() {
System.out.println("User.run("+i+")");
}
public static void main(String[] args) {
for(int i=1;i<100;i++){
User user = new User(i);
Thread t = new Thread(user);
t.start();
}
}
}
使用的时候实例化一个Thread,并传入自己的类实例,执行start()方法,与上文extends Thread同理。
3.实现Callable接口 - 有返回值
自己的类实现Callable接口,通过FutureTask包装器来创建Threa线程
public class User implements Callable {
private int i;
public User(int i) {
this.i = i;
}
//重写的call()方法有返回值,可以通过FutureTask.get()来获取
@Override
public Object call() throws Exception {
System.out.println("User.run("+i+")");
return i;
}
public static void main(String[] args){
List<FutureTask> ftList = new ArrayList<>();
//先弄出来一堆线程
for(int i=1;i<1000;i++){
User user = new User(i);
FutureTask ft = new FutureTask(user);
Thread thread =new Thread(ft);
thread.start();
ftList.add(ft);
}
//调用get方法获取这一堆线程的返回值,注意:get方法是阻塞的(即:线程无返回结果,get方法会一直等待。)
ftList.forEach(e->{
try {
System.out.println(e.get().toString());
} catch (Exception ex) {
ex.printStackTrace();
}
});
}
}
4.使用ExecutorService
使用Executors类可以创建4种类型的线程池
1.Executors.newCachedThreadPool()
创建可缓存的线程池,调用execute将重用以前构造的线程(如果线程可用),如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有60 秒钟未被使用的线程。
2.Executors.newFixedThreadPool(int nThreads)
创建固定长度的线程池。
3.Executors.newSingleThreadExecutor()
创建一个单线程的Executor。
4.Executors.newScheduledThreadPool(int corePoolSize)
创建一个支持定时及周期性的任务执行线程池,多数情况下可用来替代Timer类。
线程池常用启动方法有两个:
1.有返回值的:submit()方法
传递一个Callable,或Runnable,返回Future。
2.没有有返回值的:execute()方法
传递一个Runnable
无返回值的Code (实现Runnable,调用execute()方法)
//无返回值的Code
public class User implements Runnable {
private int i;
public User(int i) {
this.i = i;
}
@Override
public void run() {
//可以分别尝试四种线程池,打印线程的名称,来看到四种线程池的区别
Thread thread = Thread.currentThread();
System.out.println("User.run("+i+")" + "ThreadName:"+thread.getName());
}
public static void main(String[] args){
ExecutorService service = Executors.newCachedThreadPool();
//先弄出来一堆线程
for(int i=1;i<100;i++){
User user = new User(i);
service.execute(user);
}
}
}
有返回值的Code,(实现Callable,调用submit()方法)
//有返回值的Code
public class User implements Callable {
private int i;
public User(int i) {
this.i = i;
}
@Override
public Object call() throws Exception {
Thread thread = Thread.currentThread();
System.out.println("User.run("+i+")" + "ThreadName:"+thread.getName());
return i;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(20);
//先弄出来一堆线程
for(int i=1;i<100;i++){
User user = new User(i);
Future future = service.submit(user);
//同理,get()方法是阻塞的
System.out.println(future.get().toString());
}
}
}
备注:一般在项目中用线程池的话会被配成一个全局的,直接拿过来使用即可
springboot配置几个bean,使用的时候 @Resource注入进来即可。
点击这个跳转-项目线程池配置类