一、线程创建的5种基本用法
1.定义一个类来继承Thread类
首先创建一个类来继承Thread,然后重写Thread类中的run方法。
然后在main函数里创建MyThread类的实例,然后通过start方法来创建和启动线程
2.实现Runnable接口
首先创建一个类来实现Runnable接口,然后重写run方法
然后在main函数中创建Thread类的实例, 调用Thread的构造方法时将Runnable对象作为参数传递,然后通过start方法来创建和启动线程
3. 通过匿名内部类创建Thread子类对象
在main函数中创建Thread类的实例,然后创建匿名内部类重写run方法,通过start方法来创建和启动线程
4.通过匿名内部类创建 Runnable子类对象
在main函数创建Thread实例,调用Thread的构造方法时将Runnable对象作为参数传递,使用匿名类创建Runnable子类对象,然后通过start方法来创建和启动线程
5.通过lambda表达式创建Runnable子类对象
在main函数创建Thread实例,调用Thread的构造方法时将Runnable对象作为参数传递,使用lambda表达式创建Runnable子类对象,然后通过start方法来创建和启动线程
二、 线程的休眠
通过调用sleep方法来休眠当前线程
在上述代码中,在t1线程中的循环里,通过调用Thread类的静态方法sleep使得循环每循环一次都会休息1s,sleep '()' 里的单位是ms(1s = 1000ms),sleep函数需要try/catch来处理异常
三、线程的等待
在操作系统中,多线程的执行是随机调度,抢占式执行的过程,所以我们无法确定哪个线程先被调度,哪个线程先执行完,通过线程等待就是来确定线程结束的顺序。线程等待顾名思义就是一个线程等待另一个线程,通过join方法来实现线程的等待。例如现在有两个线程t1和t2,在t2线程中通过t1.join()就是为了让t2等待t1,这样如果t2先线程执行完了就不会结束线程,而是会等待t1线程结束后,t2线程才结束。具体代码如下:
运行结果:
从上述代码可以看出,t1线程中的for循环运行5次休息5秒,而t2线程的for循环运行2次,休息2买秒 ,t1和t2线程并发执行后按理来说应该是t2线程先结束,但由于在t2线程中有t1.join(),这使得t2线程执行完后会等待t1线程结束后才结束,所以才有了运行结果中的t1线程先结束然后t2线程才结束的结果。
在join方法的中存在有带参数的join方法,在上述代码中的t1.join()如果改成t1.join(2000)(单位为ms),就说明t2线程只会等待t1线程2s,如果t1线程2s后没有执行结束,那t2线程就执行结束,不再等待t1线程。
四、获取线程实例
在Java中通过currentThread()方法来获取线程实例,该方法为Thread的静态方法,具体用法如下:
运行结果:
通过currentThread()方法可以获得当前线程的引用,通过上述代码可以看出,使用currentThread()方法获取到了主线程main的引用。
五、线程中断
在Java中一个结束一个线程是比较温柔的过程,要是一个线程t2想让一个线程t1中断,不是强制让t1线程结束执行,这样就会导致t1线程的程序执行了一半,此时t1线程执行代码的结果数据就是半成品,这样的数据没有任何用处,我们希望的是要不就执行结束,要不就不执行。所以如果t2线程想让t1线程提前结束,就需要让t1线程的run方法更快执行结束。目前常见的两种方式:
1.通过共享的标记来进⾏沟通
代码运行结果:
在上述代码中使用了共享的标记isTrue,当线程t1执行时,由于isTrue 为true,在t1线程中的run方法一直在循环,当2s后t2线程执行到isTrue = false,把共享标志改了,导致t1线程中的run方法循环结束,导致run方法也结束了,使得t1线程结束,这就是通过共享标志让t2线程结束t1线程。
2.调用interrupt()方法
在上述代码中,由于在lambda表达式中不知道引用t所以需要curreThread()方法来获取当前线程引用,然后调用Thread的成员isInterrupted(这个成员是boolean类型,初始值为false),然后在main线程中通过调用interrupt()方法来设置标志位,把isInterrupt改成true,则线程t的的循环就结束了,就使得t线程的run方法结束,这就是通过Interrupt()方法来让线程提前结束。
运行结果:
从上述图片来看,运行结果与我们分析的不一样,通过interrupt(),没有使t线程结束执行,而且还抛出一个异常,这是因为在t线程的run方法里,主要的时间都在执行sleep(1000),所以当用t.interrupt()来设置标志位的时候,线程t大概率在执行sleep(1000),而interrupt()方法会唤醒sleep,这使得sleep在休眠的时候被唤醒,从而抛出异常。而且sleep被唤醒后又会清空刚才设置的标志位(刚刚把isInterrupt设为的true又清空为false),导致线程没有结束继续执行。要想真正的结束继承需要在sleep的catch里加上break或return。
运行结果 :