目录
一,线程创建
线程的创建一共有五种方法.接下来我将一一演示.
1.1 继承Thread类,重写run方法
public class ThreadDemo4 {
public static void main(String[] args) {
Thread t = new MyThread();
t.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println("MyThread");
}
}
这就是继承Thread类,重写run方法来创建线程的方法.其实就是创建了一个新的类,继承Thread,然后重写方法即可.
1.2 实现Runnable接口,重写run方法
public class ThreadDemo5 {
public static void main(String[] args) {
Mythread2 myThread = new Mythread2();
Thread t = new Thread(myThread);
t.start();
}
}
class Mythread2 implements Runnable{
@Override
public void run() {
System.out.println("Mythread");
}
}
这个其实就是,创建一个类来实现Runnable接口中的run方法.然后创建一个Mythread对象,将Mythread对象传入Thread的构造方法中去,这种方法不推荐使用,使用起来比较繁琐.
1.3 使用匿名内部类,继承Thread
public class ThreadDemo6 {
public static void main(String[] args) {
Thread t = new Thread(){
@Override
public void run() {
System.out.println("Thread");
}
};
t.start();
}
}
这个是使用匿名内部类来继承Thread类的方法,实际上和第一种的方法没有本质区别.
1.4 使用匿名内部类,实现Runnable接口
public class ThreadDemo7 {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread");
}
});
t.start();
}
}
这个方法也是用了匿名内部类的形式,来实现了Runnable接口.很简单,没有什么好说的.
1.5 使用lambda表达式
public class ThreadDemo8 {
public static void main(String[] args) {
Thread t = new Thread(()->{
System.out.println("Thread");
});
t.start();
}
}
使用lambda表达式这个方法最简单,是推荐使用的.实际上就是在()中使用lambda实现run方法.
二,线程中断
在线程里,我们有的时候想要让一个线程中断.这时候我们就可以使用interrupt这个方法.来通知线程中断.
代码如下:
public class ThreadDemo8 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
while(!Thread.currentThread().isInterrupted()){
System.out.println("Thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
Thread.sleep(3000);
t.interrupt();
}
}
我们看上述代码,首先我们创建了一个线程,其中run方法中的while的判断条件是 !Thread.currentThread().isInterrupted().其中,Thread.currentThread()这个方法是用来获取到当前线程的引用对象.哪个线程调用它,就可以获取到哪个线程的对象引用.而 isInterrupted这个方法是用来判断线程是否被终止的.在while内部,我们看到每隔一秒就打印一个Thread.
在main中,t.start创建了线程.然后让main线程睡3秒钟.接着调用t.interrupt方法来通知线程终止.
但是我们看到的结果却不尽如人意,我们希望我们在调用t.interrupt方法之后,线程就终止不再打印Thread了.但是我们看到,在报了一个异常之后,我们的线程并没有终止,而是继续打印了Thread,这是怎么回事?
在调用interrupt的时候,会把线程内部的标志位设置成true,并且当线程如果正在进行sleep,就会触发异常,把sleep唤醒.但是在唤醒sleep的时候,还会做一件事,就是把刚才设置的标志位再设置成false(清空了标志位).这就导致了在catch了sleep的异常之后,线程还在继续执行.
这样就导致了,在调用了interrupt方法之后,只是通知t线程你要终止了.但是终不终止,在于t.你通知了,我不一定要遵守呗.因此,啥时候要结束这个线程,都是由自己线程内部自身的代码来决定的.主动权就交到了程序员的手里.
注意:1.当线程在sleep中休眠的时候,此时调用interrupt会把t线程唤醒.此时interrupt方法就触发了sleep中的异常.导致sleep提前返回.
2. interrupt会使标志位为true.
3.sleep被唤醒的时候,会把线程的标志位清空.
那我们该如何去控制线程的结束呢?
1) 主动跳出循环(加break)
如果在上述代码中加上一个break(如图),那么就可以主动跳出循环.
2) 稍后进行终止
三,线程等待
当我们想让一个线程等待另一个线程执行结束,那么我们就可以使用join来进行操作.
代码如下:
public class ThreadDemo9 {
public static void main(String[] args) {
Thread t = new Thread(()->{
for(int i = 0;i<3;i++){
System.out.println("Thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
System.out.println("join之前");
//此处的join可以使当前的main线程来等待t线程执行结束.
try {
t.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("join之后");
}
}
本来执行完start之后,t线程和main线程就会并发执行,分头行动,main和t都往下执行.但是当我们使用了join,那么main线程就发生了阻塞,main线程就会一直阻塞到线程执行结束,main线程才会从join中恢复,才能继续往下执行.
此时main线程是一直等到t线程彻底执行完毕之后才继续往下执行,否则就不执行,这叫做死等.
在join()里面,也可以给定参数,指定一个最大等待时间.
四,线程休眠
线程休眠就是利用sleep方法,来使线程进行固定时间的休眠,此时线程处于TIMED_WAITING状态.
代码如下:
public class ThreadDemo10 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
for(int i = 0;i<10;i++){
System.out.println("Thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
Thread.sleep(3000);
System.out.println("Main");
}
}
上述代码中,t线程中我们循环打印Thread,每打印一次休眠1秒钟,在main线程中,我们先start线程,接着使main线休眠3秒.接着打印Main.这里我们就发现,线程调度本身是抢占实执行的,但是此刻main线程会等待t线程3秒钟,接着再继续执行.