1.线程创建
线程的创建有许多方法,这里我们主要讲三种。
1.继承Thread类
class MyThread extends Thread { @override public void run{ System.out,println("线程运行时执行的代码"); } } MyThread t = new MyThread();//创建一个实例 t.start();//调用start方法,启动线程
2.实现Runnable接口
class MyRunnable implements Runnable { @override public void run { System.out.println("线程执行时执行的代码"); } } Thread t = new Thread(new Runnable);//将Runnabled对象作为Thread构造方法的参数 t.start();//调用start方法,启动线程,
3.lambda表达式
Thread t = new Thread(() -> { System.out.println("线程运行时执行的代码"); });//lambda本质上是一个匿名函数,主要用来实现“回调函数”效果,这里不多做拓展。
2.线程中断
在Java中,要销毁或者是终止一个线程就需要想办法让run方法尽快执行完毕。我们可以在代码中手动创建一个标志位,来作为run的执行结束的条件。事实上,很多线程执行得很久,是因为陷入了循环,所以我们跳出循环就好了。我们来看如下代码:
public class Main { private static boolean isQuit = false; public static void main(String[] args) throws InterruptedException { Thread t = new Thread(() -> { while (!isQuit) { System.out.println("线程工作中");//此处语句可执行其他任意任务 try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } System.out.println("线程工作完毕"); }); t.start(); Thread.sleep(5000); isQuit = true;//若没有此语句,循环就会一直执行下去 System.out.println("设置isQuit为true"); } }
上述方案是一个可行的方案,但是不够完美。
1.需要我们手动创建一个变量
2.当线程内部在sleep时,主线程修改变量,新线程内部不能及时响应
事实上,线程中断还有其他方法,见如下代码:
class Main{ public static void main(String[] args) throws InterruptedException { Thread t = new Thread(() -> { //Thread类中有现成的标志位,来判断当前的循环是否要结束 while (!Thread.currentThread().isInterrupted()) { //Thread.currentThread()-->获取当前线程的实例(Thread),实际上结果就是经常用的t //isInterrupted(),Thread内部的一个标志位,可以用来判断线程是否要结束 System.out.println("线程工作中"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); break;//此处加break的原因见下文 } } }); t.start(); Thread.sleep(5000); System.out.println("让t线程终止"); t.interrupt();//此语句就是把上述标志位设置为true } }
此外,利用此种方法,即使线程内部的逻辑出现阻塞,也是可以用这个方法唤醒的。
Thread.sleep(1000);
正常来说,sleep会休眠到时间到才会结束,而此处的interrupt则可以在sleep内部触发一个异常,从而被提前唤醒。这个时候又会出现一个问题,sleep因被提前触发异常而唤醒后,t线程并没有结束。事实上,sleep抛出异常后,会清除之前设置的标志位。为什么这样设定呢?Java是期望当前线程收到“要中断”的信号时,它能够自由决定接下来怎么处理。怎么处理呢?我们可以加一个break。(不妨再回头看看代码?)
3.线程等待
什么是线程等待?简单来说,就是让一个线程等待另一个线程执行结束再继续执行。本质上就是为了控制线程结束的顺序。我们可以利用join实现线程等待效果。
注意:主线程中,调用t.join,此时就是主线等待 t线程 先结束。
我们看如下代码:
class Main { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(() -> { for(int i = 0; i < 5; i++) { System.out.println("t线程执行中"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } }); t.start(); //让主线程等待t线程执行结束,一旦调用join,主线程就会进入阻塞状态。=直到t线程执行结束 t.join(); System.out.println("主线程执行"); } }
值得注意的是,调用join时如果相应的线程已经结束了,那么主线程就不会进入阻塞状态。
现在我们再来假设一个场景,倘若t线程陷入了一个死循环,难道主线程要一直等待下去吗?答案自然是否定的。join还有一个带参数的构造方法,可以设置等待的时间。
4.线程休眠
线程休眠一般用sleep方法,前面已经使用过很多次了,此处就不再赘述。不过一个值得注意的点是,利用sleep方法休眠线程1000毫秒,并不一定就是正正好好的1000毫秒,由于还存在调度的开销,可能会多那么个几毫秒。