创建多线程的四种方式及代码、FutureTask的get方法原理(实现Callable接口情况下)

本文详细介绍了Java中Thread、Runnable、Callable接口的使用,以及ExecutorService、FutureTask在处理并发任务中的应用,包括线程池的创建和不同执行方法的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

借鉴:https://blog.youkuaiyun.com/u012965203/article/details/106988910
https://blog.youkuaiyun.com/wrcyplaoye/article/details/130448605

一:继承Thread类
定义一个类继承Thread类
重写run方法:里面写线程要运行的任务代码
创建Thread子类对象
调用start方法:开启线程并调用run方法

Thread的构造方法:
Thread()
Thread(Runnable target)等

 
package com.zzq.thread;
 
public class MyThread {
 
	public static void main(String[] args) {
 
		// 创建Thread的子类对象,创建线程。
		Demo d1 = new Demo("张三");
		Demo d2 = new Demo("李四");
		d1.start(); // 开启线程,编号从0开始;
		d2.start();
 
		for (int i = 0; i < 10; i++) {
			System.out.println("主函数:" + Thread.currentThread().getName() + "...i=" + i);
		}
	}
}
 
// 定义一个类继承Thread
class Demo extends Thread {
 
	private String name;
 
	Demo(String name) {
		this.name = name;
	}
 
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(name + ":" + Thread.currentThread().getName() + "...i=" + i);
		}
	}
 
}

二:实现Runnable接口
定义子类实现Runnable接口
子类中重写run方法:将线程的任务代码封装到run方法中;
创建实现子类的对象
通过Thread类创建线程对象,并将该子类对象作为构造器的参数进行传递
调用Thread类的start方法

 
package com.zzq.thread;
 
public class MyRunnable {
 
	public static void main(String[] args) {
 
		// 3.创建实现类对象
		Demo2 d = new Demo2();
		// 4.通过Thread类创建线程对象
		Thread t1 = new Thread(d);
		// 5. 调用start方法
		t1.start();
 
		for (int i = 0; i < 10; i++) {
			System.out.println("主函数:" + Thread.currentThread().getName() + "...i=" + i);
		}
	}
}
 
// 1. 定义子类实现Runnable接口
class Demo2 implements Runnable {
 
	// 2、子类中重写run方法:将线程的任务代码封装到run方法中;
	@Override
	public void run() {
		show();
	}
 
	public void show() {
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + "...i=" + i);
		}
	}
}

三:实现Callable接口
创建Callable的实现类
重写call方法,将线程的任务代码封装到call方法中
创建Callable接口实现子类的对象;
创建FutureTask的对象,将此Callable接口实现类的对象作为构造器的参数进行传递
创建Thread对象,并调用start()。将FutureTask的对象作为参数传递到Thread类的构造器中
获取Callable中call方法的返回值。 get() 返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值

FutureTask类其实也实现了Runnable接口
(1)FutureTask implements RunnableFuture
接口:interface RunnableFuture extends Runnable, Future

FutureTask类的构造方法
FutureTask(Callable callable)

 
package com.zzq.thread;
 
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
 
public class MyCallable {
 
	public static void main(String[] args) {
		// 3、创建Callable接口实现子类的对象
		Demo3 d = new Demo3();
		// 4、创建FutureTask的对象,将此Callable接口实现类的对象作为构造器的参数进行传递
		FutureTask futureTask = new FutureTask(d);
		// 5、创建Thread对象
		Thread thread = new Thread(futureTask);
		thread.start();
		Object sum;
		try {
			//6、获取Callable中call方法的返回值
			sum = futureTask.get();
			System.out.println("总和是:" + sum);
		}
		catch (InterruptedException e) {
			e.printStackTrace();
		}
		catch (ExecutionException e) {
			e.printStackTrace();
		}
	}
 
}
 
//1、创建Callable的实现类
class Demo3 implements Callable {
 
	//2、重写call方法,将线程的任务代码封装到call方法中
	@Override
	public Object call() throws Exception {
		// 遍历1-20打印出偶数之和
		int sum = 0;
		for (int i = 1; i <= 20; i++) {
			if ((i & 1) == 0) { // i%2 == 0
				System.out.println(i);
				sum += i;
			}
		}
		return sum;
	}
 
}

FutureTask原理:

FutureTask有一个volatile的state变量,最初始的状态是new 新建状态,其他状态有completing完成中,normal正常执行完,还有其他取消、中断、异常之类的状态。

FutrueTask的get会先判断state的值是否大于comleting,也就是执行完,是的话就判断state为normal正常的话,就返回outcome结果,为cancel取消的话,就抛取消异常,为异常的话,就抛出outcome记录的异常。

如果state的值表示还没执行完的话,就会进入一个自旋操作,也就是一个死循环。

FutureTask的静态变量:

public class FutureTask<V> implements RunnableFuture<V> {
    
    private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;
    private Callable<V> callable;
    //结果返回值:是一个Object对象
    private Object outcome; 
    ........

}

FutureTask的get()方法:volatile修饰的state变量值> COMPLETING = 1 就返回outcome对象

public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

FutureTask的awaitDone()方法:for 循环自旋判断volatile修饰的state变量值

private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        WaitNode q = null;
        boolean queued = false;
        for (;;) {
            if (Thread.interrupted()) {
                removeWaiter(q);
                throw new InterruptedException();
            }

            int s = state;
            if (s > COMPLETING) {
                if (q != null)
                    q.thread = null;
                return s;
            }
            else if (s == COMPLETING) // cannot time out yet
                Thread.yield();
            else if (q == null)
                q = new WaitNode();
            else if (!queued)
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                     q.next = waiters, q);
            else if (timed) {
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {
                    removeWaiter(q);
                    return state;
                }
                LockSupport.parkNanos(this, nanos);
            }
            else
                LockSupport.park(this);
        }
    }

四:使用线程池

线程工厂的四种构造方法:
(1)newSingleThreadExecutor :Executors.newSingleThreadExecutor(); 单线程化的线程池
(2)newFixedThreadPool :Executors.newFixedThreadPool(3); 定长线程池
(3)newCachedThreadPool : Executors.newCachedThreadPool();可缓存的线程池
(4)newScheduledThreadPool :Executors.newScheduledThreadPool(2); 定时、周期性执行定长线程池

线程池的执行方法:
(1)execute(Runnable)
(2)submit(Runnable)
(3)submit(Callable)
(4)invokeAny(Collection<? extends Callable> tasks) :例如List<Callable>
(5)invokeAll(Collection<? extends Callable> tasks):例如List<Callable>
(6):shutdown()
(7):shutdownNow()

方法继承关系:Executors的构造方法创建
Executors.newSingleThreadExecutor();
Executors.newFixedThreadPool();
Executors.newCachedThreadPool();
Executors.newScheduledThreadPool();

public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}

ThreadPoolExecutor类:
ThreadPoolExecutor extends AbstractExecutorService抽象类
AbstractExecutorService抽象类实现ExecutorService接口
ExecutorService接口继承Executor接口

ExecutorService接口
(2)Future submit(Runnable task, T result);
(3)Future submit(Callable task);
(4)T invokeAny(Collection<? extends Callable> tasks) :例如List<Callable>
(5)List<Future> invokeAll(Collection<? extends Callable> tasks):例如List<Callable>
(6):void shutdown()
(7):List shutdownNow()

Executor接口
(1)void execute(Runnable command)

1.ExecutorService的execute方法使用:参数是Runnable类型

		ExecutorService executor = Executors.newFixedThreadPool(10);

		executor.execute(new Runnable() {
			public void run() {
    			System.out.println("入门小站");
			}
		});

		executor.shutdown();
		ExecutorService executor = Executors.newFixedThreadPool(10);
        executor.execute(()->{
                System.out.println("入门小站");
        });

        executor.shutdown();

2.ExecutorService的submit方法使用:参数可以是Runnable和Callable类型

public static void main(String[] args) {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(new MyCallable());
        System.out.println("主线程执行其它业务逻辑");
        // 4. 阻塞式获取异步线程结果
        try {
            String result = future.get();
            System.out.println("阻塞获取线程执行结果:" + result);
        }catch (Exception e){
            System.out.println(e.toString());
        }
    }

    public static class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            String name = Thread.currentThread().getName();
            System.out.println(name + "新线程执行任务");
            Thread.sleep(1000);
            return name;
        }
    }
    
控制台输出:
主线程执行其它业务逻辑
pool-1-thread-1新线程执行任务 (等待1000ms后才打印了下面一行)
阻塞获取线程执行结果:pool-1-thread-1

3.ExecutorService的invokeAll方法和invokeAny方法的使用:
参数是Collection<? extends Callable> 类型

invokeAll:提交一组异步事务, 会阻塞主线程, 等这一组事务全部完成之后, 才会继续主线程。 返回值是List<Future>类型, 返回每个异步任务的执行结果
invokeAny:提交的一组异步任务, 只有一个返回结果, 当任一异步任务执行结束后,会强制取消其它异步任务。返回值是T类型,代表其中一个结果。

invokeAll方法:

public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        List<Callable<String>> list = new ArrayList<>();
        list.add(new MyCallable(1000L));
        list.add(new MyCallable(2000L));
        list.add(new MyCallable(3000L));
        // 3. 提交一组异步任务, 主线程调用future.get()方法会被阻塞
        try {
            List<Future<String>> futures = executorService.invokeAll(list);
            System.out.println("主线程被阻塞, 不能执行其它任务,直到上面的所有任务执行完才会打印此行");
            for (Future<String> future : futures) {
                System.out.println(future.get());
            }
        }catch (Exception e){
            System.out.println(e.toString());
        }
        executorService.shutdown();
    }

    public static class MyCallable implements Callable<String> {
        Long time = 0L;
        public MyCallable(Long time) {
            this.time = time;
        }

        @Override
        public String call() throws Exception {
            String name = Thread.currentThread().getName();
            System.out.println(name + "新线程执行任务");
            Thread.sleep(time);
            return name;
        }
    }
控制台打印:

pool-1-thread-2新线程执行任务
pool-1-thread-3新线程执行任务
pool-1-thread-1新线程执行任务
主线程被阻塞, 不能执行其它任务,直到上面的所有任务完成才会打印次行
pool-1-thread-1
pool-1-thread-2
pool-1-thread-3

invokeAny方法:

public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        List<Callable<String>> list = new ArrayList<>();
        list.add(new MyCallable(1000L));
        list.add(new MyCallable(2000L));
        list.add(new MyCallable(3000L));
        // 3. 提交一组异步任务, 主线程调用future.get()方法会被阻塞
        try {
            String str = executorService.invokeAny(list);
            System.out.println("主线程被阻塞, 不能执行其它任务,直到上面有一个任务执行完才会打印此行");
            System.out.println(str);

        }catch (Exception e){
            System.out.println(e.toString());
        }
        executorService.shutdown();
    }

    public static class MyCallable implements Callable<String> {
        Long time = 0L;
        public MyCallable(Long time) {
            this.time = time;
        }

        @Override
        public String call() throws Exception {
            String name = Thread.currentThread().getName();
            System.out.println(name + "新线程执行任务");
            Thread.sleep(time);
            return name;
        }
    }

控制台打印:

pool-1-thread-1新线程执行任务
pool-1-thread-2新线程执行任务
pool-1-thread-3新线程执行任务
主线程被阻塞, 不能执行其它任务,直到上面有一个任务执行完才会打印此行
pool-1-thread-1

4.ExecutorService的shutdown方法和shutdownNow方法的使用:

(1)shutdown:调用 shutdown() 方法之后线程池并不是立刻就被关闭。这时线程池中可能还有很多任务正在被执行,或是任务队列中有大量正在等待被执行的任务,调用 shutdown() 方法后线程池会在执行完正在执行的任务和队列中等待的任务后才彻底关闭,调用 shutdown() 方法后如果还有新的任务被提交,线程池则会根据拒绝策略直接拒绝后续新提交的任务。
(2)shutdownNow:在执行 shutdownNow() 之后,首先会给所有线程池中的线程发送 interrupt 中断信号,尝试中断这些任务的执行,然后会将任务队列中正在等待的所有任务转移到一个 List 中并返回,我们可以根据返回的任务 List 来进行后续补救操作(先记录落库,需要时取出来进行重试之类的)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值