1.创建和运行线程
方法一:直接使用Thread
@Slf4j
public class Test1 {
public static void main(String[] args) {
Thread t = new Thread(){
@Override
public void run(){
log.debug("t1线程running");
}
};
t.setName("t1"); //为线程指定名称,也可以用构造来指定new Thread("t1")
t.start();
log.debug("主线程running");
}
}
方法二:使用Runnable配合Thread
把【线程】和【任务】分开,实现Runnable接口run方法写任务代码,创建Thread对象,通过构造传递runnable参数,达到创建线程、并实现任务的目的。
Thread代表线程,Runnable可运行的任务(线程要执行的代码)
@Slf4j
public class Test2 {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
log.debug("t2-running");
}
};
Thread thread = new Thread(runnable,"t2");
thread.start();
log.debug("main-running");
}
}
lambda表达式写法(如果接口只有一个成员方法,该接口会被@FunctionalInterface注解标注,表示该成员方法支持函数表达式写法,见下图,idea中可以使用快捷键Alt+Enter来帮你生成)。
上面方法二的函数表达式写法:
@Slf4j
public class Test2 {
public static void main(String[] args) {
Runnable runnable = ()->{log.debug("t2-running");};
Thread thread = new Thread(runnable,"t2");
thread.start();
log.debug("main-running");
}
}
原理之Threa与Runnable对象的关系
通过刨析源码,实际上,利用Thread构造方法来传递runnable对象的方法,实际上在方法的内部,将runnable对象赋值给了Thread的成员变量target ,target这个成员变量,实际上在Thread对象中调用了run方法,所以,方法二实际上走的还是Thread对象的run方法,与方法一直接重载run方法在本质上是一致的。
小结:
方法一是把线程和任务合并在一起了,方法二是把线程和任务分开了
用Runnable更容易与线程池等高级API配合
用Runnable让任务类脱离了Thread继承体系,更加灵活(Java里组合优于继承)
方法三:FutureTask配合Thread
FutureTask能够接收Callable类型的参数,用来处理有返回结果的情况
@Slf4j
public class Test3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> futureTask = new FutureTask<>(() -> {
log.debug("t1-running");
Thread.sleep(1000);
return 100;
});
Thread thread = new Thread(futureTask,"t1");
thread.start();
log.debug("main-running,{}",futureTask.get());
}
}