- 通过显示继承Thread类的方式来实现
代码实现:
package Java0509;
/**
* @author wgsstart
* @creat 2021-05-09 16:42
*/
public class ThreadDemo1 {//在这个代码中其实涉及到两个线程,MyThread创建出的线程,main方法对应的的主线程
static class MyThread extends Thread {
@Override
public void run() {
System.out.println("hello,world,我是一个线程");
}
}
public static void main(String[] args) {
/*
* 创建线程需要使用Thread类,来创建一个Thread的实例,
* 另一方面还需要给这个线程指定要执行哪些指令/代码,
* 指定指令的方式有很多种,此处先用一种简单的,直接继承Thread类,重写Thread类中的run方法。*/
Thread t = new MyThread();//[注意!]:当Thread对象被创建出来的时候,内核中并没有随之产生一个线程(PCB)
t.start();//执行这个start方法,才是真的创建出了一个线程,此时内核中才随之出现了一个PCB,这个PCB就会对应让CPU来执行该线程的代码(上面的run方法中的逻辑)
}
}
- 可以通过匿名内部类的方式继承Thread类
代码实现:
package Java0509;
/**
* @author wgsstart
* @creat 2021-05-09 17:43
*/
public class ThreadDemo2 {
private static long count = 100_0000_0000L;//在一个较大的整数中适当的插入一些下划线,下划线不具备任何意义,但是能提高代码的可读性。
public static void main(String[] args) {
// serial();//串行,大概时间28秒
concurrency();//并发,大概时间11秒
}
private static void concurrency() {
long beg = System.currentTimeMillis();//时间戳
Thread t1 = new Thread() {
/*匿名内部类:创建了一个没有名字的类,只知道这个类继承自Thread。{}中是这个类的具体代码,同时也会new出来一个
* 这个类的实例*/
@Override
public void run() {
int a = 0;
for (long i = 0; i < count; i++) {
a++;
}
}
};
Thread t2 = new Thread() {
/*匿名内部类:创建了一个没有名字的类,只知道这个类继承自Thread。{}中是这个类的具体代码,同时也会new出来一个
* 这个类的实例*/
@Override
public void run() {
int b = 0;
for (long i = 0; i < count; i++) {
b++;
}
}
};
t1.start();
t2.start();
try {
t1.join();//线程等待,让主线程等待t1和t2执行结束,然后再继续往下执行。
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
/*t1,t2和main线程之间都是并发执行的,调用了t1.start 和 t2.start之后,两个新线程正在紧锣密鼓的进行计算过程中,
* 此时主线程仍然会继续执行,下面的end就随之被计算了,正确的做法应该是要保证t1和t2都计算完毕,再来计算这个end的时间戳*/
long end = System.currentTimeMillis();
System.out.println("time: " + (end - beg) + "ms");
}
private static void serial() {//串行29秒
long beg = System.currentTimeMillis();//时间戳
int a = 0;
for (long i = 0; i < count; i++) {
a++;
}
int b = 0;
for (long i = 0; i < count; i++) {
b++;
}
long end = System.currentTimeMillis();
System.out.println("time: " + (end - beg) + "ms");
}
}
- 显示创建一个类,实现Runnable接口,然后把这个Runnable的实例关联到Thread实例上。
package Java0509;
import java.util.TimerTask;
/**
* @author wgsstart
* @creat 2021-05-09 20:16
*/
public class ThreadDemo3 {
//Runnable本质上就是描述了一段要执行的任务代码是啥
static class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("我是一个新线程");
}
}
public static void main(String[] args) {
//3.显示创建一个类,实现Runnable接口,然后把这个Runnable的实例关联到Thread实例上。
Thread t = new Thread(new MyRunnable());
t.start();
}
}
4.通过匿名内部类的方式来实现Runnable接口
package Java0509;
import java.util.TimerTask;
public class ThreadDemo3 {
public static void main(String[] args) {
//4.通过匿名内部类的方式来实现Runnable接口
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("我是一个新线程");
}
};
Thread t = new Thread(runnable);
t.start();
}
}
5.使用lambda表达式来指定线程执行的内容
package Java0509;
import java.util.TimerTask;
public class ThreadDemo3 {
public static void main(String[] args) {
//5.使用lambda表达式来指定线程执行的内容
Thread t = new Thread(() -> {
System.out.println("我是一个新线程");
});
t.start();
}
}
这五种创建线程的方式,没有本质上的区别(站在操作系统的角度),核心都是依靠Thread类,只不过指定线程执行的任务的方式有所差异。{
站在代码耦合性的角度,细节上有点区别,通过Runnable/lambda的方式来创建线程和继承Thread类相比,代码耦合性要小一些。在写Runnable或者lambda的时候,run中没有涉及到任何的Thread相关内容,这就意味着很容易把这个逻辑从多线程中剥离出来,去搭配其他的并发编程的方式来执行,当然也可以很容易改成不并发的方式来执行。
}