如何创建、运行java线程,实践和思考
实践是检验真理的唯一标准,以下代码和理论都是认真整理,实践所得.
有三种实现方法,
1. 第一种继承Thread类
2. 第二种是实现Runnable接口,两者都去重写run()方法
3. JDK1.5以后,增加有用线程程池创建多线程的方式,是java.util.concurrent包中的内容,此节分析前两种方式,第三种不做讨论,
1. 继承Thread
普通写法与匿名写法,因为Java中单继承的原因,此种方法有他的耦合性和局限,new Thread()建立子类对象的实例时,新线程也被创建,start()开启线程.
/**
* <p>
* 第一种方式,继承Thread的普通和匿名写法
* </p>
*/
public class MyThread001 extends Thread {
@Override
public void run() {
System.out.println("第一种启动线程的普通方法,直接继承thread,单一继承的局限");
}
public static void main(String[] args) {
Thread myThread = new Thread(new MyThread001());
myThread.start();
}
匿名的方式的最大不同
写法一:匿名Thread的实例对象,执行MyThread001的run()方法,涉及两个类,Thread类和MyThread001,其实就是上述的连写
new Thread(new MyThread001()).start();
写法二:匿名Thread的实例对象,同时,还包含一个MyThread001的匿名子类,总共涉及三个类,一个继承了MyThread001的匿名子类,一个MyThread001,一个Thread
new Thread(new MyThread001(){
@Override
public void run() {
System.out.println("我是继承MyThread001的匿名子类,需要注意哦");
}
}).start();
}
2.实现Runnable接口
第二种方式,实现Runnable接口的普通和匿名写法,匿名写法的区别和继承Thread的区别是一样的。清楚有哪些匿名内部类。new Thread(new MyThread003()).start()
new的是一个MyThread003对象实例,但是new Thread(new MyThread003(){}).start();
new的是一个继承了MyThread003的匿名子类。
/**
* <p>
* 第二种方式,实现Runnable接口的普通和匿名写法
* </p>
*/
public class MyThread003 implements Runnable {
@Override
public void run() {
System.out.println("第二种启动线程的普通方法,去实现Runnable接口");
}
public static void main(String[] args) {
Thread thread = new Thread(new MyThread003());
thread.start();
new Thread(new MyThread003()).start();
new Thread(new MyThread003(){}).start();
}
}
3.匿名方式写法不同,背后的实现方式也是不同的。
研究和汇总分析(核心)
写法一:继承方式的匿名写法
/**
* <p>
* 两种方式的匿名写法
* </p>
*/
public class MyThread002{
public static void main(String[] args) {
/*
* <p>
* 继承Thread的匿名子类
*
* new Thread(){}.start();这表示调用继承Thread父类的子类对象的run方法,
* new Thread(){}表示一个继承了Thread的匿名子类的实例对象
* </p>
*/
new Thread(){
@Override
public void run() {
System.out.println("第一种继承Thread的匿名写法01,匿名类,匿名子类还是重写run方法");
}
}.start();
写法二:实现Runnable接口的匿名写法
/*
* <p>
* 实现Runnable接口的匿名子类
*
* new Thread(new Runnable(){}).start();
* 这表示调用Thread对象接受的Runnable对象的run
* 表示:new Runnable(){}表示一个实现了Runnable接口的匿名子类的实例对象
* </p>
*/
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("第一种继承Thread的匿名写法02,匿名类,匿名子类还是重写run方法");
}
}).start();
}
方式三: lambda表达式写法(写法不同,实现的方式是相同的)
/*
* <p>
* lambda表达式,是通过实现Runnable的方式
* </p>
*/
///通过 Lambda 表达式创建 Runnable 接口的引用
Runnable r = () -> System.out.println("通过 Lambda 表达式创建 Runnable 接口的引用");
new Thread(r).start();
// ||
// ||
// \/
/*
* <p>
* 当不指明函数式接口时,编译器会自动解释这种转化
* 在上面的代码中,编译器会自动推断:根据线程类的构造函数签名 public Thread(Runnable r) { },
* 将该 Lambda 表达式赋给 Runnable 接口
* </p>
*/
new Thread(() -> System.out.println("Lambda01,第二种继承Runnable匿名写法01的Lambda写法")).start();
/*
* <p>
* Java 中的 Lambda 表达式通常使用 (argument) -> {body},body内如果是单句可以省略
* new Thread(()->{}).start();
* </p>
*/
new Thread(()->{System.out.println("Lambda表02{}:Java 中的 Lambda
表达式通常使用 (argument) -> {body},body内如果是单句可以省略");}).start();
}
4.run()和start()方法的区别
我们来看看下面的测试,这个时候你会困惑,到底哪种方式才是启动线程呢
调用run()和start()时,你并不会感觉到有什么不妥,因为run()方法的确像我们想的那样被调用,编译也不会报错
但是,事实上,run()方法并非由新建的新线程所执行的,而是被新创线程的当前线程所执行。
只有调用了start()方法,才会表现出多线程的特性,异步方式,run()执行时,仍然是当前线程,同步方式.想要由创建的新线程,而不是由当前线程执行run()方法,必须使用新线程(newThread)的start()方法.
/**
* <p>
* 分析run和start的区别
* </p>
*
* @author:180285
* @date: 2018/8/24 9:43
*/
public class MyThread004 implements Runnable{
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("当前线程名 : " + threadName +" 分析run和start的区别......");
}
public static void main(String[] args) {
MyThread004 myThread004 = new MyThread004();
myThread004.run(); //当前线程名 : main 分析run和start的区别......
Thread thread01 = new Thread(new MyThread004(),"新开辟的线程0");
thread01.run(); //当前线程名 : main 分析run和start的区别......
thread01.start();//当前线程名 : 新开辟的线程0 分析run和start的区别......
}
}
另一种分析
和上面是完全不同的,下面是一个继承了MyThread004的匿名子类,且run方法直接继承的super.run();另一个MyThread004的匿名类重写了run()方法
Thread thread02 = new Thread(new MyThread004(){});
thread02.run();// main 分析run和start的区别......
thread02.start();//Thread-0 分析run和start的区别......
Thread thread03 = new Thread(new MyThread004(){
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("当前线程名 : " + threadName
+ " 一个继承了MyThread004的匿名子类,且重写run方法");
}
});
thread03.run();//当前线程名 : main 一个继承了MyThread004的匿名子类,且重写run方法
thread03.start();//当前线程名 : Thread-1 一个继承了MyThread004的匿名子类,且重写run方法