Java多线程(一)- 基础

255 阅读2分钟

除去线程池,我们应该都知道java多线程的实现方式有三种:继承Thread类,实现Runnable接口,实现Callable接口。本文暂不涉及线程池,主要说一下以上三种方式。

第一种:继承Thread类

Thread类在java.lang包中。我们只需要继承Thread类,重写run()方法,就可以实现多线程了。 示例代码如下:

public class ThreadDemo extends Thread {    
    private String name;    
    public ThreadDemo(String name) {        
        this.name = name;    
    }    
    public void run() {        
        System.out.println("thread name is:" + name);    
    }    
    public static void main(String[] args) {        
        ThreadDemo t1 = new ThreadDemo("first thread");
        ThreadDemo t2 = new ThreadDemo("second thread");
        ThreadDemo t3 = new ThreadDemo("third thread");
        t1.start();
        t2.start();
        t3.start();
    }
}

执行结果如下(执行结果可能会不同,为何不同,可以思考一下):

thread name is:first thread
thread name is:third thread
thread name is:second thread

看过Thread源码的都知道,Thread类是实现了Runnable接口的。

第二种:实现Runnable接口

Runnable仅仅是一个接口,它只包含run()方法,源码如下:

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

实现示例如下:

public class RunnableDemo {
    public static void main(String[] args) {
        RunnableImpl r1 = new RunnableImpl("first runnable");
        RunnableImpl r2 = new RunnableImpl("second runnable");
        RunnableImpl r3 = new RunnableImpl("third runnable");
        new Thread(r1).start();
        new Thread(r2).start();
        new Thread(r3).start();
    }
}
class RunnableImpl implements Runnable {
    public String name;
    public RunnableImpl(String name){
        this.name = name;
    }
    @Override
    public void run() {
        System.out.println("runnable name is:" + name);
    }
}
//lambda 实现方式更简单
new Thread(() ->{
    String name = "first runnable";
    System.out.println("runnable name is:" + name);
}).start();

以上两种启动线程的方式原理是一样的。首先都是调用本地方法启动一个线程,其次是在这个线程里执行目标对象的run()方法。那么这个目标对象是什么呢?我们来看看Thread类的run()方法的实现是:

public void run() {
    if (target != null) {
        target.run();
    }
}

如果采用的是继承Thread类的方式,那么这个target就是线程对象自身,如果采用的是实现Runnable接口的方式,那么这个target就是实现了Runnable接口的类的实例。

第三种:实现Callable接口

与 Runnable 相比,Callable 可以有返回值,返回值通过 FutureTask 进行封装。 Callable位于java.util.concurrent包下,它也是一个接口,在它里面也只声明了一个方法,只不过这个方法叫做call():

public interface Callable<V> {
    V call() throws Exception;
}

这是一个泛型接口,call()方法返回的类型就是传递进来的V类型。 实现示例如下:

public class CallableDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Integer> implCallable = new CallableImpl();
        FutureTask<Integer> futureTask = new FutureTask<>(implCallable);
        new Thread(futureTask).start();
        long start = System.currentTimeMillis();
        System.out.println("start...."+ start);
        System.out.println("result:"+futureTask.get());
        long end = System.currentTimeMillis();
        System.out.println("end......"+ end +",cost:"+ (end - start) +"ms");
    }
}

class CallableImpl implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int result = 0;
        for(int i=0;i<10;i++){
            result += i;
        }
        Thread.sleep(2000);
        return result;
    }
}

思考:FutureTask的使用方式。