创建线程的方式
方式1:匿名内部类
@Slf4j(topic = "c.Test1")
public class Test1 {
public static void main(String[] args) {
Thread t1 = new Thread(){
@Override
public void run() {
log.debug("t1 start");
}
};
t1.setName("t1");
t1.start();
log.debug("running");
}
}
执行结果:
对于线程来说,创建线程之后还要使用.start()方法来启动它
方式2:使用Runnable接口
@Slf4j(topic = "c.Test2")
public class Test2 {
public static void main(String[] args) {
Runnable r =new Runnable() {
@Override
public void run() {
log.debug("hello!");
}
};
Thread t =new Thread(r, "t2");
t.start();
}
}
执行结果:
在这里可以看到,我们可以使用Runnable接口,在接口中实现run的抽象方法,再把对象r交给线程,同时也可以用线程自带的构造方法给它命名。
Runnable接口:
源码:
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
我们可以看到,Runnable接口只有一种抽象方法。所以我们可以在这个接口上加上“@FunctionalInterface”注解,这个注解标注的接口被称为“函数式接口”,这种接口可以用lambda方法简化。
lambda表达式:
如下:
@Slf4j(topic = "c.Test2")
public class Test2 {
public static void main(String[] args) {
Runnable r =()-> {log.debug("hello!");};
Thread t =new Thread(r, "t2");
t.start();
}
}
如果只有一行语句还可以像这样把花括号去掉:
@Slf4j(topic = "c.Test2")
public class Test2 {
public static void main(String[] args) {
Runnable r =()-> log.debug("hello!");
Thread t =new Thread(r, "t2");
t.start();
}
}
异同:
在源码上,我们先看方法二的Runnable对象究竟去了哪里:
查看方法2Thread方法:发现Runnable给了init方法
点击init方法向下找发现:
然后我们可以找到:
target如果不为空我们就执行target的.run()方法。
也就是说方法二其实是用的是Thread的内部方法。
而方法一其实是创建了一个子类对象,在子类对象里重写了Thread的run方法。
但是无论那种方法,其实都是调用了Thread的run方法。
小结:
方法2,将任务(Runnable)与线程(Thread)分离了,这种写法更加灵活,也更推荐这种写法。
用 Runnable 更容易与线程池等高级 API 配合。
用 Runnable 让任务类脱离了 Thread 继承体系,更灵活。