线程创建的四种方式

创建线程方式

方式一、创建一个任务类实现Runnable接口,并将其具体对象提交给Thread构造器

创建一个发射类LiftOff实现Runnable接口:

package concurrency;

public class LiftOff implements Runnable {
    protected int countDown = 10; // Default
    private static int taskCount = 0;
    private final int id = taskCount++;

    public LiftOff() {
    }

    public LiftOff(int countDown) {
        this.countDown = countDown;
    }

    public String status() {
        return Thread.currentThread() + "#" + id + "(" + (countDown > 0 ? countDown : "Liftoff!") + "), ";
    }

    public void run() {
        while (countDown-- > 0) {
            System.out.println(status());
            Thread.yield();
        }
    }
} 

以上代码中调用了Thread.yield()方法,该方法的作用是建议线程调度器切换到其它线程执行任务,注意,只是建议,不保证采纳;

创建完任务类之后,可以在Main函数中使用LiftOff对象创建一个Thread对象,并调用其start方法启动该线程,如下:

package concurrency;

public class BasicThreads {
    public static void main(String[] args) {
        Thread t = new Thread(new LiftOff());
        t.start();
        System.out.println(Thread.currentThread() + "Waiting for LiftOff");
    }
} 

打印结果如下,注意该程序中是同时存在两个线程(main和Thread-0)在运行的;

另外关于Thread对象的打印形式为[Thread-0,5,main],其中依次代表[线程名,线程优先级、线程组名], 具体可查看Thread类的toString方法;

Thread[main,5,main]Waiting for LiftOff
Thread[Thread-0,5,main]#0(9), 
Thread[Thread-0,5,main]#0(8), 
Thread[Thread-0,5,main]#0(7), 
Thread[Thread-0,5,main]#0(6), 
Thread[Thread-0,5,main]#0(5), 
Thread[Thread-0,5,main]#0(4), 
Thread[Thread-0,5,main]#0(3), 
Thread[Thread-0,5,main]#0(2), 
Thread[Thread-0,5,main]#0(1), 
Thread[Thread-0,5,main]#0(Liftoff!), 

方式二、继承Thread类,调用其具体对象的start方法

package concurrency;

public class SimpleThread extends Thread {
    private int countDown = 5;
    private static int threadCount = 0;

    public SimpleThread() {
        // Store the thread name:
        super(Integer.toString(++threadCount));
        start();
    }

    public String toString() {
        return "#" + getName() + "(" + countDown + "), ";
    }

    public void run() {
        while (true) {
            System.out.println(this);
            if (--countDown == 0)
                return;
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++)
            new SimpleThread();
    }
}

对比通过实现Runnable接口的方式,该方式不建议使用,因为java的单继承机制,通常通过实现接口比继承会更好点;

另外还可以通过内部内部类将线程代码隐藏在类中,如下写法;

class InnerThread1 {
    private int countDown = 5;
    private Inner inner;

    private class Inner extends Thread {
        Inner(String name) {
            super(name);
            start();
        }

        public void run() {
            try {
                while (true) {
                    print(this);
                    if (--countDown == 0)
                        return;
                    sleep(10);
                }
            } catch (InterruptedException e) {
                print("interrupted");
            }
        }

        public String toString() {
            return getName() + ": " + countDown;
        }
    }

    public InnerThread1(String name) {
        inner = new Inner(name);
    }
}

方式三、创建一个任务类实现Runnable接口,并将其具体对象提交给Executors【推荐】

java.util.concurrent包中的执行器Executors可以帮助我们管理Thread对象,简化并发编程,如下,可以使用Executors类中的newCachedThreadPool静态方法创建一个可缓存的线程池,并用其执行相关任务;

package concurrency;

import java.util.concurrent.*;

public class CachedThreadPool {
    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++)
            exec.execute(new LiftOff());
        exec.shutdown();
    }
}

在Executors类中,除了通过newCachedThreadPool创建线程池外,还可以创建通过以下方法创建其它种类的线程池:

newFixedThreadPool:固定大小度的线程池

newSingleThreadExecutor:单线程线程池

newScheduledThreadPool:执行定时和周期性任务

方式四、创建一个任务类实现Callable接口,并将其具体对象提交给Executors【推荐】

实现Callable接口的类同样是一个任务类,与实现Runnable接口的区别是该方式可以有返回值;

在实现Callable接口的类中,线程执行的方法是call方法(有返回值),而不是run方法;

在main方法中可以通过调用ExecutorService的submit方法,返回一个Future对象,通过该对象可以获取线程运行的返回值,注意需要等Future完成后才能取得结果,可以通过isDone方法来查询Future是否已完成,或者直接调用get方法来获取(会阻塞,直到结果准备就绪)。

package concurrency;

import java.util.concurrent.*;
import java.util.*;

class TaskWithResult implements Callable<String> {
    private int id;

    public TaskWithResult(int id) {
        this.id = id;
    }

    public String call() {
        return "result of TaskWithResult " + id;
    }
}

public class CallableDemo {
    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        ArrayList<Future<String>> results = new ArrayList<Future<String>>();
        for (int i = 0; i < 10; i++)
            results.add(exec.submit(new TaskWithResult(i)));
        for (Future<String> fs : results)
            try {
                System.out.println(fs.get());
            } catch (InterruptedException e) {
                System.out.println(e);
                return;
            } catch (ExecutionException e) {
                System.out.println(e);
            } finally {
                exec.shutdown();
            }
    }
} 

小结

其实,更普遍的,我觉得创建线程就两种形式:

  • 直接通过new Thread创建线程(可传入任务对象);
  • 创建任务对象提交给Executors去创建(其实内部的线程工厂也是通过new Thread创建);

另外,这里的任务对象也有两种方式创建,通过实现Runnable接口和实现Callable接口;

猜你喜欢

转载自blog.csdn.net/adrian_dai/article/details/79438236