Java线程的创建方法

来源:互联网 发布:wap论坛源码 编辑:程序博客网 时间:2024/05/01 19:18

  在实际的多线程编程中,有哪些创建线程的方式?本文从JDK提供的各种API进行讲述,水平有限,还请指正。

1 基础方式

  之所以称之为基础方式,是因为后面的方式都是基于基础方式,做一些封装完成的。很多文章也称之为传统方式,主要是相对于JDK1.5之后的并发工具包而言。

1.1 继承Thread类

  继承Thread类,重写run方法,将线程的执行逻辑放到run方法中,完毕!通过实例的start方法启动。

public class MyThread extends Thread{    @Override    public void  run(){        System.out.println("拜托,你倒是在这写上业务逻辑啊!");    }    public static void main(String[] args) {        new MyThread().start();    }}output:拜托,你倒是在这写上业务逻辑啊!
1.2 实现Runnable接口

  实现Runnable接口,并将其实例mt作为参数传入到Thread的构造函数中生成Thread实例。start方法实际上是调用了mt的run方法。

public class MyThread implements Runnable(){    @Override    public void run(){        System.out.println("拜托,你倒是在这写上业务逻辑啊!");    }    public static void main(String[] args){        MyThread mt = new MyThread();        new Thread(mt).start();    }}output:拜托,你倒是在这写上业务逻辑啊!
1.3 两者对比

(1)继承性:Java是单继承机制,所以采用 1.1 形式创建线程后,则不能再继承其它类,而 1.2 形式则仍可以继承其它类。
(2)共享性:在多线程开发中,每个线程都可以传入同一个Runnable实例,以达到资源共享的目的,并且可以在该共享实例中做好并发的逻辑控制。

2 线程工具类创建

  工具类创建线程的方式,底层还是由上述基础方式实现的。不同的需求诞生不同的工具,使用工具类主要有以下好处。

(1)满足特定需求。开发者可以根据不同的应用场景选择不同的线程工类。
(2)简化编程模型。不用从基础方式开始构建模型,实际应用往往是多线程的,因此用基础方式创建的线程,开发者经常要为共享资源(例如,抢火车票,票数就是所有用户共享的)设计并发策略,并且要进行大量测试,而工具类则是已经经过验证的,可靠性高。
(3)封装线程生命周期。线程工具类中,更多关注的是可执行的任务,任务是更容易理解的抽象概念,开发者将很少关注线程的各种状态(例如中断、阻塞、就绪等状态)了,工具类已经这些逻辑封装好,暴露给开发者的是任务

2.1 Timer和TimerTask

  Timer和TimerTask位于java.util包下,是JDK1.3提供的。Timer是定时器类,主要是负责调度,TimerTask则是实现了Runnable接口的抽象类,是负责执行任务的类,被Timer调度。

public class Timer{    ......    //Timer类中有一个私有线程实例,用于调用Runnable实例    private TimerThread thread = new TimerThread(queque)      ......}public abstract class TimerTask implements Runnable{...}

  我认为该组合也是线程的一种创建方式,不过更符合特定的应用场景,如定时、周期执行等,所以列入文章。

public class Test{    public static void main(String[] args){        //调度器        Timer timer = new Timer();          //被调度的任务        TimerTask task = new TimerTask(){            @Override            public void run(){                System.out.println("Do my task!");            }               };        //执行调度方法,1秒后执行任务        timer.schedule(task, 1000);    }}output:Do my task!
2.2 Callable、Future和FutureTask

  通过基础方式创建线程的一个不足之处就是:无法直接获得(可以通过共享变量等方式间接获得)执行的结果,因为run方法是void类型的。

public interface Runnable{    public abstract void run();}

  而在JDK1.5后提供的Callable接口和Future接口则弥补了这一缺陷。线程执行结果由Future的实例接收,通过get方法获取。V表示的是返回类型,可在实现接口时自定义。

public interface Callable<V>{    V call() throws Exception;}public interface Future<V>{    ......    V get() throws InterruptedException,ExecutionException;    ......}

  Callable和Future是一对儿,那FutureTask又是个啥呢?咱先理解字面意思,回顾上面,TimerTask和FutureTask,很明显,都是Task,负责执行任务的类。那么FutureTask怎么被调用呢?先看定义:

public interface RunnableFuture<V> extends Runnable,Future<V>{...}public class FutureTask<V> implements RunnableFuture<V>{......    //constructor  Future和Callable是一对儿~    public FutureTask(Callable<V> callable){...}......}

  原来FutureTask终究也是个Runnable,所以在调度的角色中,属于执行任务的类。但是FutureTask和普通的任务执行类有些区别,它是既可执行,又可获取执行结果,因为它实现了Future接口。
  既然FutureTask是个Runnable,参照1.2的形式,可以直接传入Thread的构造函数中。被Thread实例直接调用了,如下。

public class CallableThreadTest{    public static void main(String args[]){        //创建Callable实例        Callable<Integer> ct = new Callable<Integer>(){            @Override            public Integer call(){                System.out.println(Thread.currentThread().getName() + "执行中");                return 100;            }        };         //封装Callable实例        FutureTask<Integer> task = new FutureTask(ct);           new Thread(task,"有返回值的线程").start();         //输出任务执行的返回结果        try{            System.out.println(task.get());        }catch(Exception e){            e.printStackTrace();        }    }}output:有返回值的线程执行中100

  上面的调用顺序是:Thread的start -》FutureTask的run-》Callable的call。细节就不多讲了,本文主要是介绍线程创建方式。

2.3 线程池

  在实际应用中经常是多个线程并发执行任务的,为了节省创建线程和销毁线程的时间,当需要执行任务的时候,从池里取一个线程,把任务传给线程,去执行就Okay了,任务执行完了,该线程暂不销毁,返回到池里,等待下一个执行任务。
  Executors类提供了四种创建线程池的静态方法,如下。

    Executors.newFixedThreadPool(int nThreads)    Executors.newCachedThreadPool()    Executors.newSingleThreadExecutor()    Executors.newScheduledThreadPool(int corePoolSize)

  那么问题来了,本文的主题是:如何创建线程?线程池中预先创建好的一些Thread实例,某种意义上只能算是空创建,因为执行任务的逻辑并没有交给Thread,需要传入进去,该线程实例才赋予了意义。
  线程池提交任务方法如下,第四种不常用。

public void execute(Runnable task)public Future<?> submit(Runnable task)public <T>Future<T> submit(Callable task)public <T>Future<T> submit(Runnable task, T result)

  很明显,提交的任务有两种,一种是无返回结果的,如下。

//创建线程池ExecutorService pool = Executors.newCachedThreadPool();//创建任务Runnable task = new Runnable(){    @Override    public void run(){        System.out.println("一去不复还");    }};//提交任务pool.execute(task);output:一去不复还

  另一种是有返回结果的,如下。

//创建线程池ExecutorService pool = Executors.newCachedThreadPool();//创建任务Callable<Integer> task = new Callable<Integer>(){    @Override    public Integer call(){        System.out.println("线程池调用我了!给你100块!");        return 100;    }   };//提交任务Future<Integer> future = pool.submit(task);//获取任务执行结果try {    System.out.println(future.get());} catch (Exception e) {    e.printStackTrace();}output:线程池调用我了!给你100块!100

  至此,总结完毕,如有描述不当,或者更好的表达方式,欢迎交流。转载请注明出处:http://blog.csdn.net/chen6581669/article/details/51579644

0 0
原创粉丝点击