Java并发编程(一)线程创建的方式

创建线程的三种方式

方法一:通过thread创建

代码:

//通过Thread创建线程
@Slf4j(topic = "c.T1")//打印日志信息,没有的可以用自带的out对象输出
public class T1 {
    public static void main(String[] args) {
        Thread t1 = new Thread("t1"){
            public void run(){
                log.debug("thread 1 running!");
            }
        };
        t1.start();
        log.debug("main running");

    }
}

输出:

14:18:23.012 [t1] DEBUG c.T1 - thread 1 running!
14:18:23.012 [main] DEBUG c.T1 - main running

方法二:通过Runnable创建

代码:

//通过 Runnable创建
@Slf4j(topic = "c.T2")
public class T2 {
    public static void main(String[] args) {
        Thread t1 = new Thread(()->{//使用lambda表达式,创建一个Runnable对象
            log.debug("Runnable 1 running !");
        },"t1");
        t1.start();
        log.debug("main running");
    }
}

输出:

14:17:13.211 [main] DEBUG c.T2 - main running
14:17:13.211 [t1] DEBUG c.T2 - Runnable 1 running !

方法三:通过FutureTask创建

代码:

@Slf4j(topic = "c.T3")
public class T3 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<Integer> ft = new FutureTask(()->{
            log.debug("FutureTask 1 running !");
            Thread.sleep(2000);//两秒后返回
            return 333;
        });
        new Thread(ft,"ft").start();
        log.debug("main running point 1");
        Integer result = ft.get();//主线程阻塞,等待ft的返回结果才继续执行
        log.debug("{}",result);
        log.debug("main running point 2");
    }
}

输出:

14:28:00.179 [main] DEBUG c.T3 - main running point 1
14:28:00.179 [ft] DEBUG c.T3 - FutureTask 1 running !
14:28:02.181 [main] DEBUG c.T3 - 333
14:28:02.182 [main] DEBUG c.T3 - main running point 2

注:

可以通过输出的左侧时间看到,确实暂停了两秒等待返回结果后才继续执行后续代码。因为FutureTask的get方法会导致线程的阻塞。

方法一和方法二的原理分析。

在第二种方法中,我们实例化了一个实现Runnable接口的匿名对象作为参数传入给了Thread的构造函数中。

Thread t1 = new Thread(()->{//使用lambda表达式,创建一个Runnable对象
    log.debug("Runnable 1 running !");
},"t1");

根据源码,这个构造方法调用了init的方法

public Thread(Runnable target, String name) {
    init(null, target, name, 0);
}

最后锁定到实际执行的init方法中,可以看到target给了对象自己的target引用赋值

同时在Thread类中的run方法是重写了自己实现接口Runnable的run方法。

Thread中:

方法一创建的thread是一个匿名内部类,重写了run,所以执行的是重写后的run方法。
方法二中执行的是target的run方法。
从中可以发现,使用第二种方法构造的时候使用的是thread中的target对象run,摆脱了继承,使用组合的方式,使得线程和任务分离。

方法三的分析

方法三是解决了线程执行结束后带返回值的问题,但他是阻塞的。

他的实现我们可以查看源码得知

FutureTask类是一个泛型类,泛型决定了它的返回值类型,同时他还实现了RunnableFuturej接口,这个接口继承了Runnable接口和Future接口,Runnable接口就说了,有一个run方法,另一个Future接口中有很多方法,其中有个V get() 方法,就是通过他得到返回值。

这个Future接口

get方法的实现延迟到具体的实现类中实现,在我们的FutureTask类中:

他的阻塞通过 awaitDone方法实现,返回值由report方法实现。

posted @ 2020-12-14 18:39  DirtyShady  阅读(63)  评论(0)    收藏  举报