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

摘要:
使用Executor,我们不需要接收线程实例来创建并将任务移交给Executor。至于将任务附加到线程以执行,或管理线程的生命周期,我们不需要处理它。

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

1.继承Thread类

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

  

public class TestThread extends Thread{

    private int count = 5;
  //创建介绍String形参的构造器,一般参数作为任务的名称,以区分不同的线程
    public TestThread(String name) {
        super(name);
    }
  //run方法体,就是要执行的任务
    public void run() {
        while (true) {
            System.out.println(this);
            if(--count == 0)
                break;
        }
    }
    //重写toString方法实现可读性更好的线程打印
    public String toString() {
        return getName()+"   count="+count;
    }

    public static void main(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 implements Runnable{

    private int count=4;

    private static int taskId = 0;

    private int id = taskId++;

    private String getStatus() {
        return "task"+id+"("+count+")";
    }
  //定义任务
    public void run() {
        while (count-->0) {
            System.out.println(getStatus());
        }
    }

    public static void main(String[] args) {
        for(int i=0;i<3;i++) {
        //将任务对象实例通过构造器传递给Thread实例,使任务附着到线程上得到执行
new Thread(new LiftOff()).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 class CacheThreadPoolTest {

    public static void main(String[] args) {
    //通过静态方法创建newCacheThreadPool的Exector,可以创建FixedThreadPool、SingleThreadExecutor等类型的Executor ExecutorService exec
= Executors.newCachedThreadPool(); for(int i=0;i<5;i++) {
       //将任务加到Executor上 exec.execute(
new LiftOff()); }
     //开始顺序关闭Executor开启的线程,之后不能再添加任务到Executor,任务执行完后相应的线程将被关闭 exec.shutdown(); } }

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

CachedThreadPool:Executor首选的线程池,通常会创建与所需数量相同的线程,在回收旧线程时停止创建新线程;
FixedThreadPool:一次性预先创建固定数量的线程,可以节省后期线程创建的开销,并且防止滥用线程;
SingleThreadExecutor:类似线程数量为1的FixedThreadPool,适用于所以任务都要在同一个线程中执行的情景(执行次序为任务被提交的先后顺序),还可以用于执行短任务;
public class LiftOff implements Runnable{

    private int count=4;

    private static int taskId = 0;

    private int id = taskId++;

    private String getStatus() {
        return "task"+id+"("+count+")";
    }

    public void run() {
        try {
            while (count-->0) {
                System.out.println(getStatus());
                Thread.sleep(2000);
            }
        }catch (Exception e) {
            e.printStackTrace(System.out);
        }
    }

}
public class SingleThreadExecutorTest {

    public static void main(String[] args) {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        for(int i=0;i<3;i++) {
            executor.execute(new LiftOff());
        }
    //这里一定要关闭线程,否则主线程将一直阻塞
     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<String>{ private static int taskId = 0; private int id = taskId++; public String toString() { return "#task"+id; } public String call() throws Exception { return this.toString(); } }
public class CallableTest {

    public static void main(String[] args) throws Exception{
        ExecutorService executor = Executors.newSingleThreadExecutor();
        List<Future<String>> futureList = new ArrayList<Future<String>>();
        for(int i=0;i<3;i++) {
       //通过executor的submit来提交,submit返回参数化的Future类型 futureList.add(executor.submit(
new TaskWithResult())); } executor.shutdown(); for(Future<String> future: futureList) {
       //Future的get可以得到任务的真正返回值 System.out.println(future.get()); } } }
输出:

#task0
#task1
#task2

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

免责声明:文章转载自《Java多线程学习之任务的创建以及在线程中执行任务》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇MyBatis查询传一个参数时报错:There is no getter for property named 'sleevetype' in 'class java.lang.Integerpython 模块相互import会出问题下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

C#函数可变参数的使用

一般情况下,函数中参数是确定的。但是在某些情况下,函数的参数个数可以根据需要改变而改变,可变参数的函数使用方法是在参数前加params。 以下是我的一个demo: 查看代码 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Tex...

Windows PowerShell初体验——.NET对象支持

我个人很少用到Linux/Unix 操作系统. 对于不少Linux/Unix管理员在系统任务操作Shell一直保持很难理解. 后来从朋友公司听说他们测试队伍中专门保留了一个脚本Scirpt Shell 测试小组. 我一时更纳闷了. 当然这个问题知道我碰到Windows PowerShell-Windows出的一套Shell工具后 才渐渐理解. Window...

vue 点击图片获取x,y坐标值

点击图片拿到x,y值 template中 <image x="0"y="0"width="100%"height="100%"xlink:href="../../assets/images/background/bg_nav.png"@click="tapMap($event)" /> js中 tapMap(e) { //cons...

ACE_Task笔记

ACE_Task封装了任务,每个任务都含有一或多个线程,以及一个底层消息队列。各个任务通过这些消息队列进行通信。 其主要成员如下: open():初始化资源 close():释放资源 activate():启动线程,可指定线程的数目 svc():线程的启动位置 putq():放置消息到任务的消息队列中 getq():从任务的消息队列中取出消息 thr_co...

多线程和CPU的关系

什么是CPU (1)         Central  Progressing  Unit 中央处理器,是一块超大规模的集成电路,是一台计算机的运算核心和控制核心。 (2)         CPU包括 运算器,高速缓冲存储器,总线。 (3)         它的工作,主要是解释计算机中的指令,和处理计算机软件中的数据。它在计算机中起着最重要的作用,构成了系...

html2javabean

这个有“标题党”之嫌了。名字但是像模像样。关于网页爬虫(就是抓取网页内容)的小工具大家都写过吧。可是一般写这样的东西都是类似完成某个简单的需求而 写的类似脚本语言的东西,一般代码不多,类似黑客程序代码风格。大家应该没有做过大型项目全部以抓取网页内容为数据源吧?我就做过这样的项目,呵呵。如果真是一个多人合作,周期较长,又是一个产品型的项目,需要维护,升级,那...