前言
Thread类创建对象的方式主要有两种,一个是Thread()构造,一个是Thread(Runnable target)构造。
先来看一下Thread类和Runnable接口的关系,Thread类实现了Runnable接口,并且Runnable是一个函数式接口,无入参,无返回值,这就说明可以直接写lambda进行灵活的实现了。
创建线程的两种方式
Thread()构造
使用Thread()构造来创建线程的话,直接在自定义的Thread类里面重写一个run方法就可以了。
public class RunnableThread {
public static void main(String[] args) {
//创建一个新的线程
Thread myThread = new MyThread();
//线程执行用start方法
myThread.start();
System.out.println("this is mainThread");
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println("this is myThread");
}
}
或者写个匿名内部类
public class RunnableThread {
public static void main(String[] args) {
//创建一个新的线程
Thread myThread = new Thread() {
@Override
public void run() {
System.out.println("this is myThread");
}
};
//线程执行用start方法
myThread.start();
System.out.println("this is mainThread");
}
}
这里有一个小的要点,我们在主线程(main方法里)来调用子线程的方法时,一般采用start()方法,而不采用run方法,如果使用run方法的话,它就是一个普通的方法而已,不是异步的,就会变成在哪个线程里执行run方法,就会让哪个线程来执行。
如下采用run方法的执行结果:
采用start方法才能起到异步的作用:
Thread(Runnable target)构造
该构造里面放了一个Runnable接口,这就意味着也可以通过lambda表达式结合策略模式来创建新的线程了。
方案一:
public class RunnableThread {
public static void main(String[] args) {
Thread myThread = new Thread(new MyRunnable());
System.out.printf("this is mainThread %s%n",Thread.currentThread().getName());
myThread.start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.printf("this is myThread %s%n",Thread.currentThread().getName());
}
}
方案二:
public class RunnableThread {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.printf("this is myThread %s%n", Thread.currentThread().getName());
}
};
Thread myThread = new Thread(runnable);
System.out.printf("this is mainThread %s%n",Thread.currentThread().getName());
myThread.start();
}
}
}
或者直接利用lambda:
public class RunnableThread {
public static void main(String[] args) {
System.out.println("this is mainThread");
new Thread(()->{
System.out.println("this is myThread");
}).start();
}
}
二者的区别
源码分析
他们的区别也很明显,就在于有没有这个Runnable的参数
如果创建线程的时候有Runnable的参数的话,才会执行Runnable的run方法,也就是通过Thread(Runnable target)方法创建的时候;否则什么也不做,也就执行Thread子类的run方法了。
二者的选择
从面向对象的角度来看,通过Thread子类来创建的方式为泛化的关系,通过Runnable接口作为入参的方式为依赖的关系,所以后者的耦合性相对于前者来说要低一些,这样来看,后者是优先采用的方式。
从线程的安全性角度来分析,一个Runnable实例可以被多个线程来使用,有可能使用的结果会超出我们的预期,比如下面这个例子,由于多个线程共享一个Runnable变量,最后的结果会是500,进行了累加
public static void main(String[] args) {
Thread thread;
Runnable runnable = new Runnable() {
private int count;
@Override
public void run() {
for (int i = 0; i < 100; i++) {
count++;
}
System.out.printf("ThreadName:%s-----%d ", Thread.currentThread().getName(), count);
}
};
for (int i = 0; i < 5; i++) {
thread = new Thread(runnable);
thread.start();
}
}
而通过Thread()的方式,每个线程的结果都是100,是线程隔离的
public static void main(String[] args) {
Thread thread;
for (int i = 0; i < 5; i++) {
thread = new Thread() {
private int count;
@Override
public void run() {
for (int i = 0; i < 100; i++) {
count++;
}
System.out.printf("ThreadName:%s-----%d ", Thread.currentThread().getName(), count);
}
};
thread.start();
}
}
执行结果为每个线程都是100
希望各位大佬多多指点,共同进步~