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
这个Future
get方法的实现延迟到具体的实现类中实现,在我们的FutureTask类中:
他的阻塞通过 awaitDone方法实现,返回值由report方法实现。