java 在主线程执行任务_Java多线程学习之任务的创建以及在线程中执行任务

传统的创建任务、驱动任务的方式

1.继承Thread类

通过继承Thead类,并重写run方法,在run方法里面编码具体的任务,调用对象的start方法驱动任务。

public class TestThread extendsThread{private int count = 5;

//创建介绍String形参的构造器,一般参数作为任务的名称,以区分不同的线程publicTestThread(String name) {super(name);

}

//run方法体,就是要执行的任务public voidrun() {while (true) {

System.out.println(this);if(--count == 0)break;

}

}

//重写toString方法实现可读性更好的线程打印publicString toString() {return getName()+" count="+count;

}public static voidmain(String[] args) {

//创建5个任务对象,并且调用start方法让线程执行任务;for(int i=0;i<5;i++) {new TestThread("#Thread "+i).start();

}

//这里往下都是当前线程(即是执行main函数这个线程),而自定义的TestThread任务都是在main线程中开启新的线程来驱动

}

}

输出:

#Thread 0 count=5

#Thread 3 count=5

#Thread 1 count=5

#Thread 2 count=5

#Thread 1 count=4

#Thread 3 count=4

#Thread 3 count=3

#Thread 0 count=4

#Thread 3 count=2

#Thread 1 count=3

...

#Thread 0 count=1

#Thread 4 count=3

#Thread 4 count=2

#Thread 2 count=2

#Thread 4 count=1

#Thread 2 count=1

从log可以看出,这些任务被调动的机会很随机,这取决于线程调度器,而调度器的调度机制又取决于运行的操作系统,即是在同一个平台上,运行多次每次都会出现不一致的结果。

2.实现Runnable接口

这种方式与继承Thread类的方式类似,任务类实现Runnable接口的run方法,但是这个任务只是被定义了,并没有产生线程的行为,所以还必须把任务类作为创建Thread对象的构造器形参,并通过调用Thread实例的start方法,使任务附着到线程中。

public class LiftOff implementsRunnable{private int count=4;private static int taskId = 0;private int id = taskId++;privateString getStatus() {return "task"+id+"("+count+")";

}

//定义任务public voidrun() {while (count-->0) {

System.out.println(getStatus());

}

}public static voidmain(String[] args) {for(int i=0;i<3;i++) {

//将任务对象实例通过构造器传递给Thread实例,使任务附着到线程上得到执行new Thread(newLiftOff()).start();

}

}

}

输出:

task1(3)

task2(3)

task0(3)

task2(2)

task1(2)

task2(1)

task0(2)

task2(0)

task1(1)

task0(1)

task1(0)

task0(0)

同继承Thread类执行线程一样,任务都是被“随机”执行。不过,这种方式下,任务与线程明确分开在不同的对象,架构比前一种要清晰。

使用Executor

从JavaSE5起,提供了执行器(Executor)来简化并发编程。使用Executor,我们不需要收到创建Thread实例,将任务交给Excutor,至于把任务附着到线程上执行,又或者管理线程的生命周期,都不需要我们处理。

public classCacheThreadPoolTest {public static voidmain(String[] args) {

//通过静态方法创建newCacheThreadPool的Exector,可以创建FixedThreadPool、SingleThreadExecutor等类型的Executor

ExecutorService exec=Executors.newCachedThreadPool();for(int i=0;i<5;i++) {

//将任务加到Executor上

exec.execute(newLiftOff());

}

//开始顺序关闭Executor开启的线程,之后不能再添加任务到Executor,任务执行完后相应的线程将被关闭

exec.shutdown();

}

}

Executor很好地分离的任务及驱动任务的线程角色,客户端只需要专注于任务定义,而线程创建管理则交给Executor来负责。下面看看几种不同的Executor的特点:

CachedThreadPool:Executor首选的线程池,通常会创建与所需数量相同的线程,在回收旧线程时停止创建新线程;

FixedThreadPool:一次性预先创建固定数量的线程,可以节省后期线程创建的开销,并且防止滥用线程;

SingleThreadExecutor:类似线程数量为1的FixedThreadPool,适用于所以任务都要在同一个线程中执行的情景(执行次序为任务被提交的先后顺序),还可以用于执行短任务;

public class LiftOff implementsRunnable{private int count=4;private static int taskId = 0;private int id = taskId++;privateString getStatus() {return "task"+id+"("+count+")";

}public voidrun() {try{while (count-->0) {

System.out.println(getStatus());

Thread.sleep(2000);

}

}catch(Exception e) {

e.printStackTrace(System.out);

}

}

}

public classSingleThreadExecutorTest {public static voidmain(String[] args) {

ExecutorService executor=Executors.newSingleThreadExecutor();for(int i=0;i<3;i++) {

executor.execute(newLiftOff());

}

//这里一定要关闭线程,否则主线程将一直阻塞

executor.shutdown();

}

}

输出:

task0(3)

task0(2)

task0(1)

task0(0)

task1(3)

task1(2)

task1(1)

task1(0)

task2(3)

task2(2)

task2(1)

task2(0)

从任务中产生返回值

无论是实现Runnable接口还是继承Thread类,都只是单独执行任务,没能返回值。要想从任务中产生返回值,需要使用Callable接口,其中重写call方法并得到参数化的Future类型的返回值。实现Callable接口的任务需要Executor使用submit来提交。

//定义任务,实现Callable,返回值类型为String

public class TaskWithResult implements Callable{private static int taskId = 0;private int id = taskId++;publicString toString() {return "#task"+id;

}public String call() throwsException {return this.toString();

}

}

public classCallableTest {public static void main(String[] args) throwsException{

ExecutorService executor=Executors.newSingleThreadExecutor();

List> futureList = new ArrayList>();for(int i=0;i<3;i++) {

//通过executor的submit来提交,submit返回参数化的Future类型

futureList.add(executor.submit(newTaskWithResult()));

}

executor.shutdown();for(Futurefuture: futureList) {

//Future的get可以得到任务的真正返回值

System.out.println(future.get());

}

}

}

输出:

#task0

#task1

#task2

需要注意的是,从Future中得到返回值时使用了get,但是get会引起阻塞直到任务完成并返回,为了避免阻塞,可以用isDone方法来查询Future是否完成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值