问题 1,代码 1 会打印出来" hello world" 吗,为什么? 问题 2,代码 2 会打印出来"hello world " 吗,为什么?
//代码 1
public class Main {
static ExecutorService service = Executors.newSingleThreadExecutor();
public static void main (String[] args) {
service.execute(()->{
while (true){
hello2();
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
}
private static void hello2() {
hello();
}
private static void hello() {
service.submit(new Runnable() {
@Override
public void run() {
hello2();
System.out.println("hello world");
}
});
}
}
//代码 2
public class Main {
static ExecutorService service = Executors.newSingleThreadExecutor();
public static void main (String[] args) {
service.execute(new Runnable() {
@Override
public void run() {
hello2();
}
});
}
private static void hello2() {
hello();
}
private static void hello() {
service.submit(new Runnable() {
@Override
public void run() {
hello2();
System.out.println("hello world");
}
});
}
}
1
yeqizhang 2023-10-21 00:28:43 +08:00 via Android
1 不会,2 不会。主要是单线程池,然后 1 那个线程无限循环一直占着坑不退出,打印 hello 的子线程都进入到队列里去了。
|
4
yeqizhang 2023-10-21 08:54:25 +08:00 via Android
你试下 hello()方法的第一行打印线程池队列大小,hello2()方法最后一行也打印
|
5
sherlockwoo 2023-10-21 09:18:12 +08:00
这两段代码,都新建了只有一个线程的线程池,代码 1 是提交一个无限循环调用 hello2 的任务,hello2 中递归调用开启新任务,代码 2 是提交一个调用 hello2 的任务。
由于只有一个线程,代码 1 的第一个任务会永远占用这个线程,其他任务一直堆积在任务队列中无法被执行 代码 2 中第一个任务执行完后就结束了,无限打印是因为递归不断提交任务引起的 如果线程池改为 Executors.newFixedThreadPool(2); 那么代码 1 也会输出 "hello world" |
6
soarchen 2023-10-21 09:38:53 +08:00
为了更好地解释这两段代码,我们首先需要了解 Java 中`ExecutorService`和线程的工作方式。
`Executors.newSingleThreadExecutor()`返回一个使用单一工作线程操作的执行程序,可以保证任务按提交的顺序执行。 #### 代码 1 1. 主线程开始执行。 2. 主线程启动一个新的线程执行`service.execute()`内的 Lambda 表达式。 3. 新线程进入一个无限循环,每次循环都会调用`hello2()`方法,然后线程休眠 1 秒。 4. `hello2()`方法只是一个简单的封装,实际调用`hello()`方法。 5. `hello()`方法中,一个新的`Runnable`任务被提交给`ExecutorService`。这意味着它请求`ExecutorService`以后某个时间点运行这个任务。但是请注意,这个执行程序是一个`newSingleThreadExecutor()`,所以它只有一个线程。 6. 而由于`service.execute()`已经持续占用这个唯一的线程(因为它在一个无限循环中),提交给执行程序的任务从不得到执行机会。 7. 因此,代码 1 永远不会打印“hello world”。 #### 代码 2 1. 主线程开始执行。 2. 主线程启动一个新线程执行`service.execute()`内的`Runnable`任务。 3. 这个新线程调用`hello2()`,然后`hello2()`调用`hello()`。 4. 和上面一样,`hello()`中提交了一个新的`Runnable`任务给`ExecutorService`。 5. 但这次,由于没有无限循环占用执行器的唯一线程,所以提交的任务会被执行。 6. 但请注意:新提交的任务在执行时又会调用`hello2()`,这意味着它会重新提交自己。因此,这个任务会无限次地重新提交并打印“hello world”。 结论: 问题 1: 代码 1 不会打印“hello world”,因为新提交的任务从未得到执行机会。 问题 2: 代码 2 会无限次地打印“hello world”,因为任务会不断地重新提交自己并得到执行。 |
7
dw2693734d 2023-10-21 10:21:54 +08:00
用 gpt4 解答一下,gpt3 不行的
|
8
xausky 2023-10-21 10:39:31 +08:00
GPT 只是语言模型,不要说这种玩玩扰扰的代码,普通代码都有可能出错。
然后这个代码为什么这样很好理解,里面其实有一些地方可以简化后也可以实现相同效果 代码 1 ``` static ExecutorService service = Executors.newSingleThreadExecutor(); public static void main (String[] args) { service.execute(()->{ while (true){ hello(); } }); } static void hello() { service.execute(() -> { hello(); System.out.println("hello world"); }); } ``` 代码 2 ``` static ExecutorService service = Executors.newSingleThreadExecutor(); public static void main (String[] args) { service.execute(()->{ hello(); }); } static void hello() { service.execute(() -> { hello(); System.out.println("hello world"); }); } ``` 可以看到只有多一个 while 和 没有 while 的区别,OP 版本的 sleep 和多函数递归属于混淆视听,其实就是 ExecutorService 只有一个线程,同时只能跑一个函数,当有 while 的时候一直不退出导致根本没有机会跑 println |
9
BarackLee OP @sherlockwoo 嗯嗯对的,代码 2 虽然不打印"hello world",但是 ThreadPool 里面的 working queue 会一直增加新的 task. 我本来以为这个代码问题, ChatGPT 3.5 能搞的定. 结果试了三次错误两次.
|
10
BarackLee OP @dw2693734d 只有 3.5 , 没有开 4 也不会开,😂
|
11
BarackLee OP @soarchen 这个是 3.5 的回答吗? 我问了三次, 他有两次都是回复错误, 说代码 1,2 都会死锁, 不过有一次答对了,和这个回答一样
|
13
dw2693734d 2023-10-21 16:16:07 +08:00
@BarackLee
看下 GPT4 的回复正确不: 问题 1:代码 1 不会打印出 "hello world"。因为代码在单线程的线程池( ExecutorService )中递归调用 hello()和 hello2(),这导致线程池中的唯一线程被无限占用,从而新提交到线程池的任务无法执行。 问题 2:代码 2 可能会打印出 "hello world",但这不是确定的。主要原因是线程池只有一个线程,如果 hello2()和 hello()的递归调用速度非常快,那么同样有可能出现类似代码 1 的问题,导致"hello world"无法打印出来。但如果递归没有无限持续,那么"hello world"会被打印出来。 |