目录
创建线程
方法1 继承Thread类,重写run()
方法2 实现Runnable接口
方法三 使用匿名内部类来创建线程
方法4 使用匿名内部类实现Runnable
方法5 使用Lambda表达式
Thread类的常见用法
1.Thread的常见构造方法
2.Thread的常见方法
3. 线程中断
4. 线程等待
5. 获取当前线程
6. 休眠线程
class MyThread extends Thread {
@Override
public void run() {
while(true) {
System.out.println("hellow thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadDemo1 {
public static void main(String[] args) {
Thread thread = new MyThread();
thread.start(); //创建一个线程
while(true) {
System.out.println("hellow main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在java标准库中提供了Thread类来表示一个线程,通过new MyThread来引用子类对象,在这个过程并没有创建线程,而真正创建进程的是通过thread.start() 方法来创建一个线程,在上述代码中包含两个线程
1. main方法中对应的线程(一个进程至少有一个线程)称为主线程
2. 通过start( )创建了一个新的线程
代码运行结果如下:
上面代码看似是死循环,通过结果会观察到是在一直交替打印hellow Thread 和hellow main,就解决了并发执行的问题,操作系统调度线程的时候,是抢占式执行,具体那个线程先执行,那个后执行是不确定的,取决于操作系统调度器,虽然有优先级,但从应用程序上看看到的效果,就好像线程之间的调度顺序是随机的。
start()和run()的区别
●start()是真正创建了一个线程,一个线程是一个独立的执行流
●run()只是描述了线程的具体操作,如果直接在main中调用run(),此时没有创建线程,只有一个main线程。
线程中的sleep()方法
静态方法: Thread.sleep(1000);
单位: ms
作用: 让当前线程进入休眠,进入“阻塞”状态,放弃占有CPU时间片,让给其他线程使用
Runnable的作用是描述一个要执行的任务,重写的run()方法就是任务的执行细节
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("hellow thread");
}
}
public class ThreadDemo2 {
public static void main(String[] args) {
//描述一个任务
Runnable runnable = new MyRunnable();
//把任务交给线程来执行
Thread t = new Thread(runnable);
t.start();
}
}
用Runnable接口来创建线程,是为了解耦合,让线程和线程要做的操作分开
这个方法本质上和方法二相同,只不过把实现Runnable的任务交给匿名内部类的语法.此处是创建了一个类,实现Runnable,同时创建类的实例,并且传给了Thread的构造方法.
把任务用lambda表达式来描述,直接把lambda传给Thread构造方法。
带两个参数的构造方法
使用匿名内部类的方法创建一个线程并且给这个线程起了个名字。
下面是thread的几个常见用法
下面中间讲解一些中断一个线程的方法
中断的意思不是让线程立即停止,而是线程A通知线程B你应该快要停止了,是否真的停止,取决于线程B这里具体的代码实现
使用标志位来控制线程是否中断
在上面代码中设置flag为标志位,当flag为false的时候,导致循环结束,进而导致这个线程结束
上面进程结束完全取决于我们线程内部的代码.
使用Thread自带的标志位来控制线程是否中断
1. Thread.currentThread()是Thread的静态方法,通过这个方法可以获取到当前线程,那个线程调用这个方法,就得到那个线程的对象的引用.
2. isInterrupted()表示线程是否被中断,为true表示被中断,为false表示未被中断.
3. 当代码执行到t.interrupt的时候,就可以把isInterrupted里的标志位进行设置为true
代码运行结果如下:
4. 如果线程在sleep中休眠,此时调用interrupt就会把t线程唤醒,interrupt会触发sleep内部的异常,导致sleep提前返回.在sleep被唤醒的时候,又会把isInterrupted()设置为false,这就导致了sleep的异常被catch完了之后代码还会继续执行.
5.要想终止线程,我们可以在catch里面加一个break,可以跳出这个循环,中断这个线程
线程是一个随机调度的过程,等待线程做的事情就是控制线程的结束顺序.
代码运行结果
当执行完t.start之后,t线程和main线程并发执行,当执行到t.join的时候,main线程发生阻塞,一直阻塞到t线程执行结束,main线程才会从join中恢复过来,继续执行.
当在join之前,t线程已经执行结束,则main线程不会阻塞,就会继续执行.
join有三种方法
使用静态方法currentThread()来获取当前线程,在那个线程中调用,就能获取到那个线程的实例
在这段代码中获取到的就是我们新创建的线程
休眠线程,本质上就是让这个线程不参与调度了,一旦线程进入阻塞状态,对应的PCB就进入阻塞状态,当调用sleep(1000)对应的线程PCB就进入阻塞队列等待1000ms当这个线程等待结束回到就绪队列,虽然sleep(1000),但实际上考虑到调度的开销,对应的线程无法再唤醒之后立刻执行,实际上间隔的时间大概率要大于等于1000ms
sleep的用法