本文简单介绍下在Java中如何创建线程,创建线程的方式。
目录
一、尝试并发运行
package com.andy.concurrent.chapter01;
import java.util.concurrent.TimeUnit;
/**
* 尝试并发测试类
* @author Andy
* @date 2020/5/24 16:33
*/
public class TryConcurrency {
public static void main(String[] args) {
browseNews();
enjoyMusic();
}
/**
* 看新闻
*/
private static void browseNews(){
for(;;){
System.out.println("新闻联播: 华为芯片7nm工艺寻求国产替代");
sleep(1);
}
}
/**
* 听音乐
*/
private static void enjoyMusic(){
for(;;){
System.out.println("王力宏:《大城小爱》");
sleep(1);
}
}
private static void sleep(int seconds){
try{
TimeUnit.SECONDS.sleep(seconds);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
这段代码试图让听音乐和看新闻两个任务同时执行,但是控制台输入的永远都是在看新闻,而听音乐的任务永远都得不到执行,TryConcurrency的代码的输出如下:
二、尝试运行交替输出
public static void main(String[] args) {
// browseNews();
// enjoyMusic();
//通过匿名内部类的方式创建线程,并且重写run方法
new Thread(){
@Override
public void run() {
enjoyMusic();
}
}.start();
browseNews();
}
console输出如下:
三、创建线程的方式
创建线程一般有如下几种方式:
1、使用Thread创建线程
package com.andy.concurrent.chapter01;
/**
* TODO
* @author Andy
* @date 2020/5/24 16:48
*/
public class Test {
public static void main(String[] args){
MyThread thread = new MyThread();
thread.start();
}
}
class MyThread extends Thread{
private static int num = 0;
public MyThread(){
num++;
}
@Override
public void run() {
System.out.println("主动创建的第"+num+"个线程");
}
}
在上面代码中,通过调用start()方法,就会创建一个新的线程了。
为了分清start()方法调用和run()方法调用的区别,请看下面一个例子:
package com.andy.concurrent.chapter01;
/**
* TODO
* @author Andy
* @date 2020/5/24 16:48
*/
public class Test {
public static void main(String[] args){
System.out.println("主线程ID:"+Thread.currentThread().getId());
MyThread thread1 = new MyThread("thread1");
thread1.start();
MyThread thread2 = new MyThread("thread2");
thread2.run();
}
}
class MyThread extends Thread{
private String name;
public MyThread(String name){
this.name = name;
}
@Override
public void run() {
System.out.println("name:"+name+" 子线程ID:"+Thread.currentThread().getId());
}
}
输出如下:
从输出结果可以得出以下结论:
1)thread1和thread2的线程ID不同,thread2和主线程ID相同,说明通过run方法调用并不会创建新的线程,而是在主线程中直接运行run方法,跟普通的方法调用没有任何区别;
2)虽然thread1的start方法调用在thread2的run方法前面调用,但是先输出的是thread2的run方法调用的相关信息,说明新线程创建的过程不会阻塞主线程的后续执行。
2、实现Runnable接口创建
class MyRunnable implements Runnable{
public MyRunnable() {
}
@Override
public void run() {
System.out.println("子线程ID:"+Thread.currentThread().getId());
}
}
public static void main(String[] args){
// System.out.println("主线程ID:"+Thread.currentThread().getId());
// MyThread thread1 = new MyThread("thread1");
// thread1.start();
// MyThread thread2 = new MyThread("thread2");
// thread2.run();
System.out.println("主线程ID:"+Thread.currentThread().getId());
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
Runnable的中文意思是“任务”,顾名思义,通过实现Runnable接口,我们定义了一个子任务,然后将子任务交由Thread去执行。注意,这种方式必须将Runnable作为Thread类的参数,然后通过Thread的start方法来创建一个新线程来执行该子任务。如果调用Runnable的run方法的话,是不会创建新线程的,这根普通的方法调用没有任何区别。
事实上,查看Thread类的实现源代码会发现Thread类是实现了Runnable接口的。
3、使用内部类创建线程
章节二中已展示,不过多赘述
在Java中,这3种方式都可以用来创建线程去执行子任务,具体选择哪一种方式要看自己的需求。直接继承Thread类的话,可能比实现Runnable接口看起来更加简洁,但是由于Java只允许单继承,所以如果自定义类需要继承其他类,则只能选择实现Runnable接口。