前言
不管是C、java、go 程序,要让程序一直不间断动行,就肯定需要保持线程不退出,才能可能持续运行。
今天说的是java,一般来说从main方法开始运行结束之后,线程也就退出,如何保证线程不退出?
实际上只要证保有一个线程在持续运行,程序就不算退出。
一般来说只需要保持main线程不退出,然后其他线程不间断的工作就OK。
实际上在如果开启多个线程,就算主线程执行结束了,子线程没有结整,JVM一样不会退出。
保持运行
上面说了,思路都量样的,就是阻塞一条线程,让JVM不要退出,一般是阻塞主线程main
,让他阻塞不退出,直到需要退出的时候再限出。
演示几种不退出的方式:
- 读取流:
System.in.read();
- 等待锁:
wait()
CountDownLatch(1).await();
- 死循环:
while(true)
- 睡眠:
sleep()
这几种方式,不是阻塞,就是睡眠,大概思路都差不多,就是
读取流
通过阻塞主线程,来验证一下,运行后就可以看到service
的run方法执行完后,程序也不会退的。
代码可以自行复制验证。
这种方式可以用,一般线上服务都是通过kill -15
来退出应用,即便是有流在等待读取,kill -15
一样也会把服务kill掉。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| import java.io.IOException;
public class BlockTest {
public static void main(String[] args) {
new Thread(new Service()).start();
try { System.in.read();
} catch (IOException e) { e.printStackTrace(); } }
}
class Service implements Runnable {
@Override public void run() { for (int i = 0; i < 100000; i++) { try { System.out.println("service: " + i); Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } }
|
线程等待
这个也好理解,就是让主线程wait
,子线程工作。
上面的阻塞也是一样的原理。只要有一条线程没退出,jvm就还会继续工作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class BlockTest {
private static Object object = new Object(); public static void main(String[] args) { try { synchronized (object) { System.out.println("开始等待"); object.wait(); System.out.println("退出"); } } catch (InterruptedException e) { e.printStackTrace(); } } }
|
死循环
这种也非常简单,但是问题是有一条线程不停的自旋,这个效率肯定不如wait
来的节约性能。当然电费不是自己交随意。
wait
的原理是依赖于操作系统的阻塞队列,系统会检查线程状态,决定是否工作,这个后面可以专门说一下wait
的r操作系统级别工作原理。
这种方式是相当的不推荐,那有没有办法,让程序可以在该阻塞的时候阻塞,该退出的时候退出,其实只能说方法还是有很多。再举个例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
public class BlockTest {
public static void main(String[] args) {
System.out.println("开始"); new Thread(new Service()).start(); while (true) { } } }
|
可控制的退阻塞
这个名字我自己起的,因为确实是可控。JDK内置了很多工具,当然都以好好利用一下。
还是一样阻塞,再唤醒。使用JUC
工具CountDownLatch
加一个标识来控制,使用await
使程阻塞,再需要的时候唤醒。
这种方式比较有效的控制线程的阻塞、运行状态给程序一个除了kill线程之外的另一个选择。我个从比较喜欢这种方式,虽然最后大部分时候退出程序都是使用的kill -15
,但是写程序就是要预留出扩展性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| import java.util.concurrent.CountDownLatch;
public class BlockTest {
static CountDownLatch countDownLatch = new CountDownLatch(1); public static void main(String[] args) { System.out.println("开始"); try { new Thread(new Service(countDownLatch)).start(); countDownLatch.await(); System.out.println("程序退出"); } catch (InterruptedException e) { e.printStackTrace(); } } }
class Service implements Runnable { private CountDownLatch countDownLatch; public Service(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; }
@Override public void run() { for (int i = 0; i < 100000; i++) { try { System.out.println("service: " + i); Thread.sleep(200); if (i == 50) { System.out.println("子线程调用 countDown"); this.countDownLatch.countDown(); } } catch (InterruptedException e) { e.printStackTrace(); } } } }
|
总结
以上就是几种保持程序不退出的方式,当然还有别的方法,上面几种方式,很多框架也使用,总的来说多了解一种多一个选择。