Java多线程Thread,Runnable, Callable<>和线程池(一)

来源:互联网 发布:金融数据挖掘工程师 编辑:程序博客网 时间:2024/05/01 02:23

这一篇主要关注于我们自己实现和管理多线程,后面会介绍使用线程池实现多线程。
Java里面实现多线程有三种方式,继承 Thead类,或者实现Runnable和Callable<>接口。下面详细介绍一下这三种实现方式。

1. Thread实现多线程

使用Thread实现,我们只需要继承Thread类,重写(overwirte)run方法。

class ThreadDemo extends Thread {    private String threadFlag;    private long sleepTime = 100;    public ThreadDemo(String threadFlag) {        System.out.println("Construct " + threadFlag);        this.threadFlag = threadFlag;    }    public ThreadDemo(String threadFlag, long sleepTime) {        System.out.println("Construct " + threadFlag);        this.threadFlag = threadFlag;        this.sleepTime = sleepTime;    }    //重写run方法    public void run() {        System.out.println("Running before: " + this.threadFlag);        try {            for (int i = 0; i < 6; i++) {                System.out.println(this.threadFlag + i);                Thread.sleep(this.sleepTime);            }        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("Running after:" + this.threadFlag);    }}

ThreadDemo类继承自Thread类,重写了Thread类的run()方法。当ThreadDemo调用start()方法的时候,run()方法会被调用。
下面是测试方法:

public class ConcurrentThread {    public static void main(String[] args) throws InterruptedException, ExecutionException {        long start = System.currentTimeMillis();        Thread tA = new ThreadDemo("Thread-A");        tA.start();        Thread tB = new ThreadDemo("Thread-B");        tB.start();        long end = System.currentTimeMillis();        System.out.println("During time: " + (end - start));    }}

测试方法里面创建了两个线程tA,tB,下面是执行的结果,可以看到tA和tB是没有先后顺序的,是两个独立的线程。“During Time:2”是主线程里面的语句,但是它在tA和tB没有执行完成的时候,已经开始执行。
说明这两个线程并没有阻塞主线程。
测试结果

2. Runnable实现多线程

另外一种实现多线程的方法是implement Runnable接口

class RunnableImpl implements Runnable {    private String threadFlag;    private long sleepTime = 100;    public RunnableImpl(String threadFlag) {        System.out.println("Construct " + threadFlag);        this.threadFlag = threadFlag;    }    public RunnableImpl(String threadFlag, long sleepTime) {        System.out.println("Construct " + threadFlag);        this.threadFlag = threadFlag;        this.sleepTime = sleepTime;    }    @Override    public void run() {        System.out.println("Running before: " + this.threadFlag);        try {            for (int i = 0; i < 6; i++) {                System.out.println(this.threadFlag + i);                Thread.sleep(this.sleepTime);            }        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("Running after:" + this.threadFlag);    }}

RunnableImpl类实现了Runnable接口,实现了run()方法,是用start的方法去启动改线程。下面是测试方法和结果:

public class ConcurrentThread {    public static void main(String[] args) throws InterruptedException, ExecutionException {        long start = System.currentTimeMillis();        Thread tA = new Thread(new RunnableImpl("Thread-A"));        tA.start();        Thread tB = new Thread(new RunnableImpl("Thread-B", 400));        tB.start();        long end = System.currentTimeMillis();        System.out.println("During time: " + (end - start));    }}

这里写图片描述

使用RunnableImpl和Thread去创建两个线程tA和tB,从结果中看到tA和tB也是并发执行的,他们是随机的获取CPU的执行时间。“During time:4”在两个线程没有执行完成的时候已经输出了。说明这两个线程也没有阻塞主线程。

3. Callable<>实现多线程

观察Thread和Runnable实现的线程,你会发现这两种实现,你是无法知道线程的执行状态的,例如:线程是否执行完成了,线程有没有被cancel等等。如果你需要基于线程的状态做些事情,就需要很多而外的工作。为了解决这些问题,Callable<>就应运而生了,它和FutureTask结合可以很好的解决这个问题。FutureTask一般有两个用途。
1.获取异步的结果和取消任务。
2. 高并发的情况下确保任务只会执行一次。

import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;class CallableDemo implements Callable<String> {    private String threadFlag;    private long sleepTime = 400;    public CallableDemo(String threadFlag) {        System.out.println("Construct " + threadFlag);        this.threadFlag = threadFlag;    }    public CallableDemo(String threadFlag, long sleepTime) {        System.out.println("Construct " + threadFlag);        this.threadFlag = threadFlag;        this.sleepTime = sleepTime;    }    @Override    public String call() throws Exception {        System.out.println("Running before: " + this.threadFlag);        Thread.sleep(this.sleepTime);        System.out.println("Running after:" + this.threadFlag);        return this.threadFlag + " success in this time";    }}

这里CallableImpl实现了Callable接口,实现了里面的call()方法,这个接口是泛型化的接口,接口的类型就是call()方法的返回值类型。下面是测试代码:

public class ConcurrentThread {    public static void main(String[] args) throws InterruptedException, ExecutionException {        long start = System.currentTimeMillis();        FutureTask<String> futureA = new FutureTask<String>(new CallableDemo("Thread-A", 5000));        Thread tA = new Thread(futureA);        tA.start();        System.out.println("Thread is Done: " + futureA.isDone());        System.out.println(futureA.get());        System.out.println("Thread is Done: " + futureA.isDone());        FutureTask<String> futureB = new FutureTask<String>(new CallableDemo("Thread-B", 400));        Thread tB = new Thread(futureB);        tB.start();        long end = System.currentTimeMillis();        System.out.println("During time: " + (end - start));    }}

这里也是实现了两个线程tA和tB,和上面Thread和Runnable的不同之处在于,这里使用futureA和futureB来获取两个线程的执行状态。futureA.isDone()。这两需要着重说的是futureA.get().这个方法的意思是:主线程等待tA执行完成。这个方法是会阻塞主线程的运行。下面是测试结果:
这里写图片描述
可以看到被标记的就是返回值,这是通过get()方法得到的,主线程会成tA执行完成之后,才会继续向下执行,而tB没有调用futureB.get(),所以tB没有阻塞主线程,和主线程是并发的。可以看到‘During time: 5003’在tB没有执行完成的时候已经被执行。

总结

这篇主要介绍了自己实现多线程的三种方式:

  1. extends Thread,重写run()方法
  2. implements Runnable, 实现run()方法
  3. implements Callable<>,实现call()方法,可以获取线程的执行状态信息,isDone(),isCancelled()

下一篇我们会介绍,线程并发时候的共享资源问题。

1 0
原创粉丝点击