目录
- 一、基础知识
- (1)进程
- (2)线程
- (3)主线程
- 二、创建子线程
- (1)通过实现Runnable接口
- (2)通过继承Thread线程类
- (3)使用匿名内部类和Lambda表达式实现线程体
一、基础知识
学习线程之前先了解一下有关的基础知识
(1)进程
一般可以在同一时间内执行多个程序的操作系统都有进程的概念。一个进程就是一个执行中的程序, 而每一个进程都有自己独立的一块内存空间、一组系统资源。在进程的概念中,每一个进程的内部数 据和状态都是完全独立的。
(2)线程
线程与进程相似,是一段完成某个特定功能的代码,是程序中单个顺序控制的流程,但与进程不同的 是,同类的多个线程是共享一块内存空间和一组系统资源。所以系统在各个线程之间切换时,开销要 比进程小的多,正因如此,线程被称为轻量级进程。一个进程中可以包含多个线程。
(3)主线程
Java程序至少会有一个线程,这就是主线程,程序启动后是由JVM创建主线程,程序结束时由JVM停 止主线程。主线程它负责管理子线程,即子线程的启动、挂起、停止等等操作。图所示是进程、 主线程和子线程的关系,其中主线程负责管理子线程,即子线程的启动、挂起、停止等操作。
获取主线程示例代码如下:
public class HelloWorld {
public static void main(String[] args) {
// 获取主线程
Thread mainThread = Thread.currentThread();
System.out.println("主线程名"+mainThread.getName());
}
}
运行结果:
主线程名main
Thread.currentThread()获得当前线程,由于在main()方法中当前线程就是主线程,
Thread是Java线程类,位于java.lang包中
getName()方法获得线程的名字,主线程名是 main,由JVM分配。
二、创建子线程
Java中创建一个子线程涉及到:
java.lang.Thread类和java.lang.Runnable接口。
Thread是线程类,创建一 个Thread对象就会产生一个新的线程。
而线程执行的程序代码是在实现Runnable接口对象的run()方法 中编写的,实现Runnable接口对象是线程执行对象。
线程执行对象实现Runnable接口的run()方法,run()方法是线程执行的入口,该线程要执行程序代码都 在此编写的,run()方法称为线程体。
注意:主线程中执行入口是main(String[] args)方法,这里可以控制程序的流程,管理其他的子线 程等。
子线程执行入口是线程执行对象(实现Runnable接口对象)的run()方法,在这个方法可以 编写子线程相关处理代码。
详细了解java.lang.Thread类和java.lang.Runnable接口见Java官方API文档
http://www.matools.com/api/java8
有以下几种方式创建子线程
(1)通过实现Runnable接口
创建步骤:
1、通过实现Runnable接口创建线程执行类
2、通过重写Runnable中的run方法,编写线程执行代码
3、创建线程Thread对象,将线程执行对象传递给它
4、开始线程
创建线程Thread对象时,可以将线程执行对象传递给它,这需要是使用Thread类如下两个构造方法:
- Thread(Runnable target, String name):target是线程执行对象,实现Runnable接口。name为线程指 定一个名字。
- Thread(Runnable target):target是线程执行对象,实现Runnable接口。线程名字是由JVM分配 的。
实现Runnable接口的线程执行对象Runner代码如下:
public class Runner implements Runnable {
// 编写执行线程的代码,重写Runnable接口中的run方法,run()方法是线程体,在该方法中编写你自己的线程处理代码。
@Override
public void run() {
for (int i=0;i<10;i++){
// 打印线程次数和线程名
System.out.printf("第%d次执行--%s\n",i,Thread.currentThread().getName());
try {
// 随机生成休眠时间
long sleepTime = (long)(1000*Math.random());
// 线程休眠
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 线程执行结束
System.out.println("执行完成"+Thread.currentThread().getName());
}
}
测试代码:
public class HelloWorld {
public static void main(String[] args) {
// 创建一个线程t1,参数是线程执行对象Runner
Thread t1 = new Thread(new Runner());
t1.start();
// 创建一个线程t2,参数是线程执行对象Runner
Thread t2 = new Thread(new Runner(),"MyThread");
t2.start();
}
}
Thread.currentThread()可以获得当前线程对象,getName()是Thread类的实例方法,可以获得线程的名。
Thread.sleep(sleepTime)是休眠当前线程,sleep是静态方法:
- static void sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠。
- static void sleep(long millis, int nanos) 在指定的毫秒数加指定的纳秒数内让当前正在执行的线程 休眠。
线程创建完成还 需要调用start()方法才能执行,start()方法一旦调用线程进入可以执行状态, 可以执行状态下的线程等待CPU调度执行,CPU调用后线程进行执行状态,运行run()方法。
运行结果如下:
第0次执行--Thread-0
第0次执行--MyThread
第1次执行--Thread-0
第1次执行--MyThread
第2次执行--MyThread
第2次执行--Thread-0
第3次执行--MyThread
第4次执行--MyThread
第3次执行--Thread-0
第4次执行--Thread-0
第5次执行--MyThread
第6次执行--MyThread
第5次执行--Thread-0
第6次执行--Thread-0
第7次执行--Thread-0
第7次执行--MyThread
第8次执行--Thread-0
第8次执行--MyThread
第9次执行--MyThread
第9次执行--Thread-0
执行完成Thread-0
执行完成MyThread
提示: 仔细分析一下运行结果,会发现两个线程是交错运行的,感觉就像是两个线程在同时运行。但是实际上一台PC通常就只有一颗CPU,在某个时刻只能是一个线程在运行,而Java语言在 设计时就充分考虑到线程的并发调度执行。对于程序员来说,在编程时要注意给每个线程执行的 时间和机会,主要是通过让线程休眠的办法(调用sleep()方法)来让当前线程暂停执行,然后由 其他线程来争夺执行的机会。如果上面的程序中没有用到sleep()方法,则就是第一个线程先执行 完毕,然后第二个线程再执行完毕。所以用活sleep()方法是多线程编程的关键。
(2)通过继承Thread线程类
事实上Thread类也实现了Runnable接口,所以Thread类也可以作为线程执行对象,这需要继承Thread类,覆盖run()方法。
创建步骤:
1、通过继承Thread线程类创建线程执行类
2、定义构造方法,通过super调用父类Thread构造方法
这两个Thread类 构造方法:
- Thread(String name):name为线程指定一个名字。
- Thread():线程名字是JVM分配的。
3、通过重写Thread中的run方法,编写线程执行代码
4、创建线程执行对象,将参数传递给它
5、开始线程
代码如下:
//线程执行对象
public class MyThread extends Thread {
// 调用了一个构造方法,通过super调用父类的构造方法
public MyThread(){
super();
}
public MyThread(String name){
super(name);
}
// 编写执行线程代码
// 重写Threa的的run方法
@Override
public void run() {
for (int i = 0; i < 10; i++) {
// 打印线程次数和线程名
System.out.printf("第%d次执行--%s\n", i,getName());
try {
// 随机生成休眠时间
long sleepTime = (long) (1000 * Math.random());
// 线程休眠
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 线程执行结束
System.out.println("执行完成" + getName());
}
}
测试代码:
public class HelloWorld {
public static void main(String[] args) {
// 创建线程t1
Thread t1 = new MyThread();
// 开始线程t1
t1.start();
// 创建线程t1
Thread t2 = new MyThread("mythread");
// 开始线程t1
t2.start();
}
}
由于Java只支持单重继承,继承Thread类的方式不能再继承其他父类。当开发一些图形界 面的应用时,需要一个类既是一个窗口(继承JFrame)又是一个线程体,那么只能采用实现 Runnable接口方式。
(3)使用匿名内部类和Lambda表达式实现线程体
如果线程体使用的地方不是很多,可以不用单独定义一个类。可以使用匿名内部类或Lambda表达式直接实现Runnable接口。Runnable中只有一个方法是函数式接口,可以使用Lambda表达式。
代码如下:
//使用匿名内部类和Lambda表达式实现线程体
public class HelloWorld {
public static void main(String[] args) {
// 创建线程t1,参数是实现Runnable接口的匿名内部类
Thread t1 = new Thread(new Runnable() {
// 编写执行线程代码
@Override
public void run() {
for (int i=0;i<10;i++){
// 打印线程次数和线程名
System.out.printf("第%d次执行--%s\n",i,Thread.currentThread().getName());
try {
// 随机生成休眠时间
long sleepTime = (long)(1000*Math.random());
// 线程休眠
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 线程执行结束
System.out.println("执行完成"+Thread.currentThread().getName());
}
});
// 开始线程t1
// 创建线程t2,参数是实现Runnable接口的Lambda表达式
Thread t2 = new Thread(() -> { //重写run方法
for (int i=0;i<10;i++){
// 打印线程次数和线程名
System.out.printf("第%d次执行--%s\n",i,Thread.currentThread().getName());
try {
// 随机生成休眠时间
long sleepTime = (long)(1000*Math.random());
// 线程休眠
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 线程执行结束
System.out.println("执行完成"+Thread.currentThread().getName());
},"MyThread");
t2.start();
}
}
匿名内部类和Lambda表达式不需要定义一个线程类文件,使用起来很方便。特别是 Lambda表达式使代码变得非常简洁。
以上内容仅供参考学习,如有侵权请联系我删除!
如果这篇文章对您有帮助,左下角的大拇指就是对博主最大的鼓励。
您的鼓励就是博主最大的动力!