一篇文章带你理解Thread(多线程)的基础用法
目录
1.线程创建
1.1通过继承类创建
1.2通过类实现接口创建
1.3通过内部类继承Thread来创建
1.4通过内部类实现Runnable接口来创建
1.5通过lambda表达式创建
2.线程中断
2.1自定义标志位
2.2interrupt()方法
3.线程等待
4.线程休眠
5.获取线程实例
1.线程创建
线程创建有五种方法,这里喜欢用那种用那种
注意:下文中的start方法才是多线程开始执行,run方法只是表明需要执行的内容
1.1通过继承类创建
class A extends Thread {
@Override
public void run() {
System.out.println("通过继承Thread类创建线程");
}
}
public class Main {
public static void main(String[] args) {
A a = new A();
//这里的start是线程开始执行的方法,执行的是Thread中的run方法
a.start();
}
}
这里需要注意,当我们继承Thread类的时候,必须重新run方法,相当于是在给线程分配任务
1.2通过类实现接口创建
class B implements Runnable{
@Override
public void run() {
System.out.println("通过实现Runnable接口来创建");
}
}
public class Main {
public static void main(String[] args) {
//这里通过Thread类型对象,对构造方法传入一个实现了Runnable接口的类来创建
Thread b = new Thread(new B());
b.start();
}
}
1.3通过内部类继承Thread来创建
public class Main {
public static void main(String[] args) {
Thread c = new Thread(){
@Override
public void run() {
System.out.println("通过匿名内部类继承Thread来创建");
}
};
c.start();
}
}
1.4通过内部类实现Runnable接口来创建
public class Main {
public static void main(String[] args) {
Thread d = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("通过匿名内部类实现Runnable接口来实现");
}
});
d.start();
}
}
1.5通过lambda表达式创建
public class Main {
public static void main(String[] args) {
Thread e = new Thread(()-> System.out.println("使用lambda表达式创建"));
e.start();
}
}
最后我们看一下所有的执行结果
总结:
可以发现,只有通过继承Thread类的时候,不需要创建Thread对象,其余的都需要创建Thread对象
2.线程中断
2.1自定义标志位
这种方法很简单,就是自己定义一个变量,用来标识线程结束,当执行某条语句,则线程结束,就不进行演示了
2.2interrupt()方法
不仅可以用自定义的标志位,我们还可以调用interrupt方法,来进行线程中断
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
//通过Thread.interrupted()判断是否存在标志位,若不存在则一直执行while,若存在则停止并且清除标志位
while(!Thread.interrupted()){
System.out.println("123");
}
});
t1.start();
Thread.sleep(1);
//通过t1.interrupt()创建标志位
t1.interrupt();
}
}
上述代码中的含义是,开始没标志位,所以while语句可以一直执行,当我们执行到t1.interrupt之后,创建了一个标志位,这样我们就拥有了标志位,所以while循环就会终止,代码的运行结果如下
可以看到我们的线程是有停止的
我们总结一些常用的标志位用法
1.Thread.interrupt() 设置标志位,若线程阻塞则抛出异常,并且清除标志位
2.Thread.interrupted() 判断是否有标志位,若存在返回true,否则返回false,最后清除标志位
3.Thread..currentThread().isInterrputed() 判断是否有标志位,若存在返回true,否则返回false,最后不清除标志位
第三个个currentThread是得到当前线程的引用,通过这个引用再去调用isInterrputed方法
可以看到,isInterrupted方法是线程实例中的方法,不是类方法,所以我们需要通过引用来调用
3.线程等待
在多线程中,一个线程等待另一个线程,通常使用jion()方法
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("别急,我还没结束呢");
}
System.out.println("我结束了");
});
t1.start();
t1.join();
System.out.println("你终于结束了");
}
}
看这段代码,就是让主线程去等待t1线程,就是通过调用t1的join()方法来实现的,看一下运行结果
可以看到,当我们的主线程走到t1.join()之后,进行了阻塞等待,然后等t1执行结束了,才开始执行主线程后面的内容,这就是线程的等待
4.线程休眠
线程休眠我们通常使用sleep方法,这个方法是存在于Thread中的类方法,需要传入一个参数,单位是毫秒,就是让当前线程阻塞等待多长时间之后,再执行,看具体实现
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
System.out.println("别急,我还没结束呢");
}
System.out.println("我结束了");
});
t1.start();
Thread.sleep(1);
System.out.println("============================================================");
}
}
看这样一段代码,我们让主线程一秒后打印一串'=',当我们运行线程的时候
可以看到,主线程等待一秒后,打印了=,但是t1并未执行结束,但是我们的主线程调用的是sleep方法,所以并不会无限制的等下去
注意:这里的sleep需要处理编译异常,
InterruptedException
我们可以像上面一样加上声明,也可以使用try{}catch来处理,这个我们在异常的时候已经说过了,就不再赘述了
5.获取线程实例
这里我们使用currentThread来获取线程的实例,这里和this有异曲同工之妙,在哪里调用的,就会获取到哪个实例,看代码实现
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
System.out.println(Thread.currentThread());
});
t1.start();
System.out.println(Thread.currentThread());
}
}
运行结果是这样的
我们可以看到,在main中的是main线程,t1是Thread-0线程,我们做个测试,如果,在主线程中通过t1来调用currentThread获取到的是哪个线程呢?
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
});
t1.start();
System.out.println(t1.currentThread());
}
}
我们可以看到,我们在主线程中通过t1去调用currentThread()方法,得到结果也是和我们预期的一样
为什么?
因为我们的currentThread()是一个静态方法,所以不论你通过什么Thread的实例调用,其实结果都是一样的,只有一个,就是当前线程的引用
以上就是多线程的一些基本使用了
感谢阅读