在这种情况下试图理解 java 中的线程
Trying to understand threads in java in this case
您好,我正在尝试理解我的代码,我在一个方法中使用了用户同步关键字,以便只有一个线程使用它。
主要class:
public class Principal {
public static void main(String[] args) {
Messenger messenger = new Messenger();
Hilo t1 = new Hilo(messenger);
Hilo t2 = new Hilo(messenger);
t1.start();
t2.start();
}
}
信使class:
public class Messenger {
private String msg = "hello";
synchronized public void sendMessage() {
System.out.println(msg + " from " + Thread.currentThread().getName());
}
}
第三个 class:
public class Hilo extends Thread {
private Messenger messenger;
public Hilo(Messenger messenger) {
this.messenger = messenger;
}
@Override
public void run() {
while (true) {
messenger.sendMessage();
}
}
}
我有这个输出:
hello from Thread-1
hello from Thread-1
hello from Thread-1
hello from Thread-1
hello from Thread-1
hello from Thread-0
hello from Thread-0
hello from Thread-1
hello from Thread-1
hello from Thread-1
hello from Thread-1
hello from Thread-0
hello from Thread-0
hello from Thread-0
hello from Thread-0
hello from Thread-0
hello from Thread-0
hello from Thread-0
...
但我是这样预期的:
hello from Thread-1
hello from Thread-0
hello from Thread-1
hello from Thread-0
hello from Thread-1
hello from Thread-0
hello from Thread-1
hello from Thread-0
hello from Thread-1
hello from Thread-0
hello from Thread-1
hello from Thread-0
hello from Thread-1
hello from Thread-0
...
我一直在思考,但我无法理解失败。
请发表您的意见。
提前致谢。
您的结果输出完成了大约 18 个周期,并进行了大约 3 次上下文切换。您的“预期”输出通过 14 个上下文切换完成了 14 个周期。看起来你得到的行为比你预期的要好 。
问题是为什么您会期望这种低效行为。考虑到需要更多的上下文切换、更多的缓存被耗尽等等,交替是最糟糕的性能。如果能找到任何方法避免它,任何明智的实施都不会这样做。
一般来说,您希望尽可能长时间地保持一个线程 运行,因为上下文切换是有成本的。一个好的实现可以平衡性能和其他优先事项,当然,但它不会无缘无故地放弃性能。
始终使用时间戳来验证发生的顺序
实际上,System.out.println
测试并发性的机制很差。这些调用实际上并没有按照它们被调用的顺序得到输出。换句话说,从不 依赖 System.out
中的出现顺序来代表实际发生的顺序。
您可以通过包含对 Instant.now()
or System.nanoTime
的调用来查看此行为。我建议 always 在顺序很重要的几乎所有类型的 testing/debugging 中添加此类调用。如果仔细查看微秒,您会看到较晚的项目出现在您的控制台上 在 较早的项目之前。
更好的建议:将您的输出放入线程安全的集合中。在测试结束时转储到控制台。
执行者服务
在现代 Java 中,我们很少需要直接解决 Thread
class。
改为使用执行程序服务。该服务由一个或多个线程池支持。该服务根据其承诺的行为处理创建和重新创建线程的需要。
使用 call
方法将您的任务写成 Runnable
, an object with a run
method, or a Callable
。
示例代码
这是我修改后的代码。
我们的单身人士Messenger
class。一个实例被两个线程共享。
提示:调用 Thread#getId
而不是 Thread#getName
来标识线程。未来 Project Loom 中的虚拟线程可能默认缺少名称。
package work.basil.example.order;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Messenger
{
final private String msg = "hello";
final List < String > outputs = new ArrayList <>( 100 ); // Need not be thread-safe for this demo, as we only touch it from within our `synchronized` method `sendMessage`.
synchronized public void sendMessage ( )
{
String output = this.msg + " from thread id # " + Thread.currentThread().getId() + " at " + Instant.now();
this.outputs.add( output );
}
}
我们的 Runnable
任务 class。它保留一个 Messenger
对象,用于每次 run
执行。
提示:与其在 while ( true )
代码中看到无休止地 运行ning,不如将 while ( ! Thread.interrupted() )
写入 运行,直到 interrupted
标志被为那个线程抛出。 ExecutorService#shutdownNow
方法可能会为我们设置该标志,使我们的线程能够自行关闭。
package work.basil.example.order;
import java.util.Objects;
public class Hilo implements Runnable
{
// Member fields
final private String id;
final private Messenger messenger;
// Constructors
public Hilo ( final String id , final Messenger messenger )
{
this.id = id;
this.messenger = Objects.requireNonNull( messenger );
}
@Override
public void run ( )
{
while ( ! Thread.interrupted() )
{
this.messenger.sendMessage();
}
}
}
一个应用 class 来练习 运行 我们的演示。
package work.basil.example.order;
import java.time.Instant;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class App
{
public static void main ( String[] args )
{
App app = new App();
app.demo();
}
private void demo ( )
{
Messenger messenger = new Messenger();
ExecutorService executorService = Executors.newFixedThreadPool( 2 );
System.out.println( "INFO - Submitting tasks. " + Instant.now() );
executorService.submit( new Hilo( "Alice" , messenger ) );
executorService.submit( new Hilo( "Bob" , messenger ) );
executorService.shutdown();
try
{
// Wait a while for existing tasks to terminate
if ( ! executorService.awaitTermination( 15 , TimeUnit.MILLISECONDS ) )
{
executorService.shutdownNow(); // Set "interrupted" flag on threads currently executing tasks.
// Wait a while for tasks to respond to interrupted flag being set.
if ( ! executorService.awaitTermination( 1 , TimeUnit.SECONDS ) )
System.err.println( "WARN - executorService did not terminate." );
}
}
catch ( InterruptedException e ) { e.printStackTrace(); }
System.out.println( "INFO - Done with demo. Results array appears next. " + Instant.now() );
int nthOutput = 0;
for ( String output : messenger.outputs )
{
nthOutput++;
System.out.println( "output " + nthOutput + " = " + output );
}
}
}
当 运行 在我的 Mac 迷你 Intel 上使用 6 个真正的核心并且没有超线程使用早期访问 Java 17 时,我看到每个线程一次有几十个输出.请注意在下面的示例中,前 3 个线程 ID 为 14,后面的 # 4-71 都是线程 ID 15。
正如 所解释的,让线程 运行 一段时间通常效率更高。
INFO - Submitting tasks. 2021-03-23T02:46:58.490916Z
INFO - Done with demo. Results array appears next. 2021-03-23T02:46:58.527018Z
output 1 = hello from thread id # 14 at 2021-03-23T02:46:58.509450Z
output 2 = hello from thread id # 14 at 2021-03-23T02:46:58.522884Z
output 3 = hello from thread id # 14 at 2021-03-23T02:46:58.522923Z
output 4 = hello from thread id # 15 at 2021-03-23T02:46:58.522956Z
output 5 = hello from thread id # 15 at 2021-03-23T02:46:58.523011Z
output 6 = hello from thread id # 15 at 2021-03-23T02:46:58.523041Z
output 7 = hello from thread id # 15 at 2021-03-23T02:46:58.523077Z
output 8 = hello from thread id # 15 at 2021-03-23T02:46:58.523106Z
output 9 = hello from thread id # 15 at 2021-03-23T02:46:58.523134Z
output 10 = hello from thread id # 15 at 2021-03-23T02:46:58.523165Z
output 11 = hello from thread id # 15 at 2021-03-23T02:46:58.523197Z
output 12 = hello from thread id # 15 at 2021-03-23T02:46:58.523227Z
output 13 = hello from thread id # 15 at 2021-03-23T02:46:58.523254Z
output 14 = hello from thread id # 15 at 2021-03-23T02:46:58.523282Z
output 15 = hello from thread id # 15 at 2021-03-23T02:46:58.523312Z
output 16 = hello from thread id # 15 at 2021-03-23T02:46:58.523343Z
output 17 = hello from thread id # 15 at 2021-03-23T02:46:58.523381Z
output 18 = hello from thread id # 15 at 2021-03-23T02:46:58.523410Z
output 19 = hello from thread id # 15 at 2021-03-23T02:46:58.523436Z
output 20 = hello from thread id # 15 at 2021-03-23T02:46:58.523466Z
output 21 = hello from thread id # 15 at 2021-03-23T02:46:58.523495Z
output 22 = hello from thread id # 15 at 2021-03-23T02:46:58.523522Z
output 23 = hello from thread id # 15 at 2021-03-23T02:46:58.523550Z
output 24 = hello from thread id # 15 at 2021-03-23T02:46:58.523583Z
output 25 = hello from thread id # 15 at 2021-03-23T02:46:58.523612Z
output 26 = hello from thread id # 15 at 2021-03-23T02:46:58.523640Z
output 27 = hello from thread id # 15 at 2021-03-23T02:46:58.523668Z
output 28 = hello from thread id # 15 at 2021-03-23T02:46:58.523696Z
output 29 = hello from thread id # 15 at 2021-03-23T02:46:58.523760Z
output 30 = hello from thread id # 15 at 2021-03-23T02:46:58.523798Z
output 31 = hello from thread id # 15 at 2021-03-23T02:46:58.523828Z
output 32 = hello from thread id # 15 at 2021-03-23T02:46:58.523858Z
output 33 = hello from thread id # 15 at 2021-03-23T02:46:58.523883Z
output 34 = hello from thread id # 15 at 2021-03-23T02:46:58.523915Z
output 35 = hello from thread id # 15 at 2021-03-23T02:46:58.523943Z
output 36 = hello from thread id # 15 at 2021-03-23T02:46:58.523971Z
output 37 = hello from thread id # 15 at 2021-03-23T02:46:58.523996Z
output 38 = hello from thread id # 15 at 2021-03-23T02:46:58.524020Z
output 39 = hello from thread id # 15 at 2021-03-23T02:46:58.524049Z
output 40 = hello from thread id # 15 at 2021-03-23T02:46:58.524077Z
output 41 = hello from thread id # 15 at 2021-03-23T02:46:58.524102Z
output 42 = hello from thread id # 15 at 2021-03-23T02:46:58.524128Z
output 43 = hello from thread id # 15 at 2021-03-23T02:46:58.524156Z
output 44 = hello from thread id # 15 at 2021-03-23T02:46:58.524181Z
output 45 = hello from thread id # 15 at 2021-03-23T02:46:58.524212Z
output 46 = hello from thread id # 15 at 2021-03-23T02:46:58.524239Z
output 47 = hello from thread id # 15 at 2021-03-23T02:46:58.524262Z
output 48 = hello from thread id # 15 at 2021-03-23T02:46:58.524284Z
output 49 = hello from thread id # 15 at 2021-03-23T02:46:58.524308Z
output 50 = hello from thread id # 15 at 2021-03-23T02:46:58.524336Z
output 51 = hello from thread id # 15 at 2021-03-23T02:46:58.524359Z
output 52 = hello from thread id # 15 at 2021-03-23T02:46:58.524381Z
output 53 = hello from thread id # 15 at 2021-03-23T02:46:58.524405Z
output 54 = hello from thread id # 15 at 2021-03-23T02:46:58.524428Z
output 55 = hello from thread id # 15 at 2021-03-23T02:46:58.524454Z
output 56 = hello from thread id # 15 at 2021-03-23T02:46:58.524477Z
output 57 = hello from thread id # 15 at 2021-03-23T02:46:58.524499Z
output 58 = hello from thread id # 15 at 2021-03-23T02:46:58.524521Z
output 59 = hello from thread id # 15 at 2021-03-23T02:46:58.524544Z
output 60 = hello from thread id # 15 at 2021-03-23T02:46:58.524570Z
output 61 = hello from thread id # 15 at 2021-03-23T02:46:58.524591Z
output 62 = hello from thread id # 15 at 2021-03-23T02:46:58.524613Z
output 63 = hello from thread id # 15 at 2021-03-23T02:46:58.524634Z
output 64 = hello from thread id # 15 at 2021-03-23T02:46:58.524659Z
output 65 = hello from thread id # 15 at 2021-03-23T02:46:58.524685Z
output 66 = hello from thread id # 15 at 2021-03-23T02:46:58.524710Z
output 67 = hello from thread id # 15 at 2021-03-23T02:46:58.524731Z
output 68 = hello from thread id # 15 at 2021-03-23T02:46:58.524752Z
output 69 = hello from thread id # 15 at 2021-03-23T02:46:58.524780Z
output 70 = hello from thread id # 15 at 2021-03-23T02:46:58.524801Z
output 71 = hello from thread id # 15 at 2021-03-23T02:46:58.524826Z
output 72 = hello from thread id # 14 at 2021-03-23T02:46:58.524852Z
output 73 = hello from thread id # 14 at 2021-03-23T02:46:58.524902Z
output 74 = hello from thread id # 14 at 2021-03-23T02:46:58.524929Z
output 75 = hello from thread id # 14 at 2021-03-23T02:46:58.524954Z
output 76 = hello from thread id # 14 at 2021-03-23T02:46:58.524975Z
output 77 = hello from thread id # 14 at 2021-03-23T02:46:58.524998Z
output 78 = hello from thread id # 14 at 2021-03-23T02:46:58.525021Z
output 79 = hello from thread id # 14 at 2021-03-23T02:46:58.525042Z
output 80 = hello from thread id # 14 at 2021-03-23T02:46:58.525075Z
output 81 = hello from thread id # 14 at 2021-03-23T02:46:58.525095Z
output 82 = hello from thread id # 14 at 2021-03-23T02:46:58.525115Z
output 83 = hello from thread id # 14 at 2021-03-23T02:46:58.525138Z
output 84 = hello from thread id # 14 at 2021-03-23T02:46:58.525159Z
output 85 = hello from thread id # 14 at 2021-03-23T02:46:58.525194Z
output 86 = hello from thread id # 14 at 2021-03-23T02:46:58.525215Z
output 87 = hello from thread id # 14 at 2021-03-23T02:46:58.525241Z
output 88 = hello from thread id # 14 at 2021-03-23T02:46:58.525277Z
output 89 = hello from thread id # 14 at 2021-03-23T02:46:58.525298Z
output 90 = hello from thread id # 14 at 2021-03-23T02:46:58.525319Z
output 91 = hello from thread id # 14 at 2021-03-23T02:46:58.525339Z
output 92 = hello from thread id # 14 at 2021-03-23T02:46:58.525359Z
output 93 = hello from thread id # 14 at 2021-03-23T02:46:58.525381Z
output 94 = hello from thread id # 14 at 2021-03-23T02:46:58.525401Z
output 95 = hello from thread id # 14 at 2021-03-23T02:46:58.525422Z
output 96 = hello from thread id # 14 at 2021-03-23T02:46:58.525452Z
output 97 = hello from thread id # 14 at 2021-03-23T02:46:58.525474Z
output 98 = hello from thread id # 14 at 2021-03-23T02:46:58.525496Z
output 99 = hello from thread id # 14 at 2021-03-23T02:46:58.525515Z
output 100 = hello from thread id # 14 at 2021-03-23T02:46:58.525533Z
output 101 = hello from thread id # 14 at 2021-03-23T02:46:58.525555Z
output 102 = hello from thread id # 14 at 2021-03-23T02:46:58.525581Z
output 103 = hello from thread id # 14 at 2021-03-23T02:46:58.525603Z
output 104 = hello from thread id # 14 at 2021-03-23T02:46:58.525625Z
output 105 = hello from thread id # 14 at 2021-03-23T02:46:58.525645Z
output 106 = hello from thread id # 14 at 2021-03-23T02:46:58.525664Z
output 107 = hello from thread id # 14 at 2021-03-23T02:46:58.525686Z
output 108 = hello from thread id # 14 at 2021-03-23T02:46:58.525705Z
output 109 = hello from thread id # 14 at 2021-03-23T02:46:58.525723Z
output 110 = hello from thread id # 14 at 2021-03-23T02:46:58.525741Z
output 111 = hello from thread id # 14 at 2021-03-23T02:46:58.525758Z
output 112 = hello from thread id # 14 at 2021-03-23T02:46:58.525783Z
output 113 = hello from thread id # 14 at 2021-03-23T02:46:58.525801Z
output 114 = hello from thread id # 14 at 2021-03-23T02:46:58.525818Z
output 115 = hello from thread id # 14 at 2021-03-23T02:46:58.525837Z
output 116 = hello from thread id # 14 at 2021-03-23T02:46:58.525855Z
output 117 = hello from thread id # 14 at 2021-03-23T02:46:58.525875Z
output 118 = hello from thread id # 14 at 2021-03-23T02:46:58.525897Z
output 119 = hello from thread id # 14 at 2021-03-23T02:46:58.525913Z
output 120 = hello from thread id # 15 at 2021-03-23T02:46:58.525931Z
output 121 = hello from thread id # 15 at 2021-03-23T02:46:58.525965Z
output 122 = hello from thread id # 15 at 2021-03-23T02:46:58.526002Z
output 123 = hello from thread id # 15 at 2021-03-23T02:46:58.526023Z
output 124 = hello from thread id # 15 at 2021-03-23T02:46:58.526050Z
output 125 = hello from thread id # 15 at 2021-03-23T02:46:58.526075Z
output 126 = hello from thread id # 15 at 2021-03-23T02:46:58.526095Z
output 127 = hello from thread id # 15 at 2021-03-23T02:46:58.526135Z
output 128 = hello from thread id # 15 at 2021-03-23T02:46:58.526169Z
output 129 = hello from thread id # 15 at 2021-03-23T02:46:58.526233Z
output 130 = hello from thread id # 15 at 2021-03-23T02:46:58.526260Z
output 131 = hello from thread id # 15 at 2021-03-23T02:46:58.526279Z
output 132 = hello from thread id # 15 at 2021-03-23T02:46:58.526297Z
output 133 = hello from thread id # 15 at 2021-03-23T02:46:58.526315Z
output 134 = hello from thread id # 15 at 2021-03-23T02:46:58.526335Z
output 135 = hello from thread id # 15 at 2021-03-23T02:46:58.526352Z
output 136 = hello from thread id # 15 at 2021-03-23T02:46:58.526370Z
output 137 = hello from thread id # 15 at 2021-03-23T02:46:58.526389Z
output 138 = hello from thread id # 15 at 2021-03-23T02:46:58.526405Z
output 139 = hello from thread id # 15 at 2021-03-23T02:46:58.526424Z
output 140 = hello from thread id # 15 at 2021-03-23T02:46:58.526441Z
output 141 = hello from thread id # 14 at 2021-03-23T02:46:58.526465Z
output 142 = hello from thread id # 14 at 2021-03-23T02:46:58.526500Z
output 143 = hello from thread id # 14 at 2021-03-23T02:46:58.526524Z
output 144 = hello from thread id # 14 at 2021-03-23T02:46:58.526552Z
output 145 = hello from thread id # 14 at 2021-03-23T02:46:58.526570Z
output 146 = hello from thread id # 14 at 2021-03-23T02:46:58.526588Z
output 147 = hello from thread id # 14 at 2021-03-23T02:46:58.526605Z
output 148 = hello from thread id # 14 at 2021-03-23T02:46:58.526621Z
output 149 = hello from thread id # 14 at 2021-03-23T02:46:58.526642Z
output 150 = hello from thread id # 14 at 2021-03-23T02:46:58.526658Z
output 151 = hello from thread id # 14 at 2021-03-23T02:46:58.526674Z
output 152 = hello from thread id # 14 at 2021-03-23T02:46:58.526696Z
output 153 = hello from thread id # 15 at 2021-03-23T02:46:58.526715Z
您的期望似乎是线程调度程序将为每个线程提供相等的、循环的、活跃的时间片。
然而,Java 语言在线程调度方面不提供任何保证,而是将该责任留给了底层操作系统。如果您的应用程序中的线程没有执行时间敏感的工作,您可以将其作为实现细节归档,并假设调度程序将尝试为每个线程公平分配 运行 时间(当然假设线程逻辑本身不会饿死其他可运行的线程)。
另请注意,您可以 assign a priority 线程,但同样,这不提供任何执行顺序保证,只是声明您的意图,因为调度最终由底层 OS.
Java 线程调度算法是决定哪个线程应该 运行ning 在任何给定时间点的算法。而这个算法只关心Runnable state.
中的线程
线程优先级的作用来了。 Java 线程可以使用 1 到 10 之间的整数值作为其优先级。您可以通过 threadName.setPriority(value).
设置线程的优先级
如果不指定,thread默认取优先级值5。
因此,在您的情况下,两个线程都具有相同的优先级值 5。
关于java调度规则,在任何给定的时间点,最高优先级线程是运行ning。但是由于一些原因不能保证这条规则(例如:JVM 尽量避免 starvation)。
现在在这里,因为你有 2 个相同优先级的线程,Java 线程调度程序随机 select 一个线程(假设 t1)并执行。当以下情况之一发生时,另一个线程(t2)将有机会被执行,
- t1 完成 t1 的 运行 方法中的任何内容并再次竞争执行。但你不能保证下一次机会是 t2 的。 (这就是你的情况)
- 你调用了t1.yield()方法。 t1 放弃处理器并给任何其他相同优先级的线程机会。好吧,t2。 (但不能总是保证)
- Preempted 优先级更高的线程。
- 系统正在使用 Time Slicing Rule 并且特定时间到期。 (同样,不能保证 t2 会是下一个)。
如果你真的想在这里获得想要的0,1,0,1“公平”输出,你可以在Messenger中的synchronized方法中写一个逻辑class。像这样;
class Messenger {
private String msg = "hello";
private boolean state; //define a state variable
synchronized public void sendMessage() {
while(state == false){
if(!Thread.currentThread().getName().equals("Thread-0")){ //if not thread-0, then go to wait queue
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else{//if thread-0 , print and change state variable value.
System.out.println(msg + " from " + Thread.currentThread().getName());
state = true;
notifyAll();
}
}
if(!Thread.currentThread().getName().equals("Thread-1")){//if not thread-1, then go to wait queue
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else{//if thread-1 , print and change state variable value.
System.out.println(msg + " from " + Thread.currentThread().getName());
state = false;
notifyAll();
}
}
}
您好,我正在尝试理解我的代码,我在一个方法中使用了用户同步关键字,以便只有一个线程使用它。
主要class:
public class Principal {
public static void main(String[] args) {
Messenger messenger = new Messenger();
Hilo t1 = new Hilo(messenger);
Hilo t2 = new Hilo(messenger);
t1.start();
t2.start();
}
}
信使class:
public class Messenger {
private String msg = "hello";
synchronized public void sendMessage() {
System.out.println(msg + " from " + Thread.currentThread().getName());
}
}
第三个 class:
public class Hilo extends Thread {
private Messenger messenger;
public Hilo(Messenger messenger) {
this.messenger = messenger;
}
@Override
public void run() {
while (true) {
messenger.sendMessage();
}
}
}
我有这个输出:
hello from Thread-1
hello from Thread-1
hello from Thread-1
hello from Thread-1
hello from Thread-1
hello from Thread-0
hello from Thread-0
hello from Thread-1
hello from Thread-1
hello from Thread-1
hello from Thread-1
hello from Thread-0
hello from Thread-0
hello from Thread-0
hello from Thread-0
hello from Thread-0
hello from Thread-0
hello from Thread-0
...
但我是这样预期的:
hello from Thread-1
hello from Thread-0
hello from Thread-1
hello from Thread-0
hello from Thread-1
hello from Thread-0
hello from Thread-1
hello from Thread-0
hello from Thread-1
hello from Thread-0
hello from Thread-1
hello from Thread-0
hello from Thread-1
hello from Thread-0
...
我一直在思考,但我无法理解失败。
请发表您的意见。
提前致谢。
您的结果输出完成了大约 18 个周期,并进行了大约 3 次上下文切换。您的“预期”输出通过 14 个上下文切换完成了 14 个周期。看起来你得到的行为比你预期的要好 。
问题是为什么您会期望这种低效行为。考虑到需要更多的上下文切换、更多的缓存被耗尽等等,交替是最糟糕的性能。如果能找到任何方法避免它,任何明智的实施都不会这样做。
一般来说,您希望尽可能长时间地保持一个线程 运行,因为上下文切换是有成本的。一个好的实现可以平衡性能和其他优先事项,当然,但它不会无缘无故地放弃性能。
始终使用时间戳来验证发生的顺序
实际上,System.out.println
测试并发性的机制很差。这些调用实际上并没有按照它们被调用的顺序得到输出。换句话说,从不 依赖 System.out
中的出现顺序来代表实际发生的顺序。
您可以通过包含对 Instant.now()
or System.nanoTime
的调用来查看此行为。我建议 always 在顺序很重要的几乎所有类型的 testing/debugging 中添加此类调用。如果仔细查看微秒,您会看到较晚的项目出现在您的控制台上 在 较早的项目之前。
更好的建议:将您的输出放入线程安全的集合中。在测试结束时转储到控制台。
执行者服务
在现代 Java 中,我们很少需要直接解决 Thread
class。
改为使用执行程序服务。该服务由一个或多个线程池支持。该服务根据其承诺的行为处理创建和重新创建线程的需要。
使用 call
方法将您的任务写成 Runnable
, an object with a run
method, or a Callable
。
示例代码
这是我修改后的代码。
我们的单身人士Messenger
class。一个实例被两个线程共享。
提示:调用 Thread#getId
而不是 Thread#getName
来标识线程。未来 Project Loom 中的虚拟线程可能默认缺少名称。
package work.basil.example.order;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Messenger
{
final private String msg = "hello";
final List < String > outputs = new ArrayList <>( 100 ); // Need not be thread-safe for this demo, as we only touch it from within our `synchronized` method `sendMessage`.
synchronized public void sendMessage ( )
{
String output = this.msg + " from thread id # " + Thread.currentThread().getId() + " at " + Instant.now();
this.outputs.add( output );
}
}
我们的 Runnable
任务 class。它保留一个 Messenger
对象,用于每次 run
执行。
提示:与其在 while ( true )
代码中看到无休止地 运行ning,不如将 while ( ! Thread.interrupted() )
写入 运行,直到 interrupted
标志被为那个线程抛出。 ExecutorService#shutdownNow
方法可能会为我们设置该标志,使我们的线程能够自行关闭。
package work.basil.example.order;
import java.util.Objects;
public class Hilo implements Runnable
{
// Member fields
final private String id;
final private Messenger messenger;
// Constructors
public Hilo ( final String id , final Messenger messenger )
{
this.id = id;
this.messenger = Objects.requireNonNull( messenger );
}
@Override
public void run ( )
{
while ( ! Thread.interrupted() )
{
this.messenger.sendMessage();
}
}
}
一个应用 class 来练习 运行 我们的演示。
package work.basil.example.order;
import java.time.Instant;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class App
{
public static void main ( String[] args )
{
App app = new App();
app.demo();
}
private void demo ( )
{
Messenger messenger = new Messenger();
ExecutorService executorService = Executors.newFixedThreadPool( 2 );
System.out.println( "INFO - Submitting tasks. " + Instant.now() );
executorService.submit( new Hilo( "Alice" , messenger ) );
executorService.submit( new Hilo( "Bob" , messenger ) );
executorService.shutdown();
try
{
// Wait a while for existing tasks to terminate
if ( ! executorService.awaitTermination( 15 , TimeUnit.MILLISECONDS ) )
{
executorService.shutdownNow(); // Set "interrupted" flag on threads currently executing tasks.
// Wait a while for tasks to respond to interrupted flag being set.
if ( ! executorService.awaitTermination( 1 , TimeUnit.SECONDS ) )
System.err.println( "WARN - executorService did not terminate." );
}
}
catch ( InterruptedException e ) { e.printStackTrace(); }
System.out.println( "INFO - Done with demo. Results array appears next. " + Instant.now() );
int nthOutput = 0;
for ( String output : messenger.outputs )
{
nthOutput++;
System.out.println( "output " + nthOutput + " = " + output );
}
}
}
当 运行 在我的 Mac 迷你 Intel 上使用 6 个真正的核心并且没有超线程使用早期访问 Java 17 时,我看到每个线程一次有几十个输出.请注意在下面的示例中,前 3 个线程 ID 为 14,后面的 # 4-71 都是线程 ID 15。
正如
INFO - Submitting tasks. 2021-03-23T02:46:58.490916Z
INFO - Done with demo. Results array appears next. 2021-03-23T02:46:58.527018Z
output 1 = hello from thread id # 14 at 2021-03-23T02:46:58.509450Z
output 2 = hello from thread id # 14 at 2021-03-23T02:46:58.522884Z
output 3 = hello from thread id # 14 at 2021-03-23T02:46:58.522923Z
output 4 = hello from thread id # 15 at 2021-03-23T02:46:58.522956Z
output 5 = hello from thread id # 15 at 2021-03-23T02:46:58.523011Z
output 6 = hello from thread id # 15 at 2021-03-23T02:46:58.523041Z
output 7 = hello from thread id # 15 at 2021-03-23T02:46:58.523077Z
output 8 = hello from thread id # 15 at 2021-03-23T02:46:58.523106Z
output 9 = hello from thread id # 15 at 2021-03-23T02:46:58.523134Z
output 10 = hello from thread id # 15 at 2021-03-23T02:46:58.523165Z
output 11 = hello from thread id # 15 at 2021-03-23T02:46:58.523197Z
output 12 = hello from thread id # 15 at 2021-03-23T02:46:58.523227Z
output 13 = hello from thread id # 15 at 2021-03-23T02:46:58.523254Z
output 14 = hello from thread id # 15 at 2021-03-23T02:46:58.523282Z
output 15 = hello from thread id # 15 at 2021-03-23T02:46:58.523312Z
output 16 = hello from thread id # 15 at 2021-03-23T02:46:58.523343Z
output 17 = hello from thread id # 15 at 2021-03-23T02:46:58.523381Z
output 18 = hello from thread id # 15 at 2021-03-23T02:46:58.523410Z
output 19 = hello from thread id # 15 at 2021-03-23T02:46:58.523436Z
output 20 = hello from thread id # 15 at 2021-03-23T02:46:58.523466Z
output 21 = hello from thread id # 15 at 2021-03-23T02:46:58.523495Z
output 22 = hello from thread id # 15 at 2021-03-23T02:46:58.523522Z
output 23 = hello from thread id # 15 at 2021-03-23T02:46:58.523550Z
output 24 = hello from thread id # 15 at 2021-03-23T02:46:58.523583Z
output 25 = hello from thread id # 15 at 2021-03-23T02:46:58.523612Z
output 26 = hello from thread id # 15 at 2021-03-23T02:46:58.523640Z
output 27 = hello from thread id # 15 at 2021-03-23T02:46:58.523668Z
output 28 = hello from thread id # 15 at 2021-03-23T02:46:58.523696Z
output 29 = hello from thread id # 15 at 2021-03-23T02:46:58.523760Z
output 30 = hello from thread id # 15 at 2021-03-23T02:46:58.523798Z
output 31 = hello from thread id # 15 at 2021-03-23T02:46:58.523828Z
output 32 = hello from thread id # 15 at 2021-03-23T02:46:58.523858Z
output 33 = hello from thread id # 15 at 2021-03-23T02:46:58.523883Z
output 34 = hello from thread id # 15 at 2021-03-23T02:46:58.523915Z
output 35 = hello from thread id # 15 at 2021-03-23T02:46:58.523943Z
output 36 = hello from thread id # 15 at 2021-03-23T02:46:58.523971Z
output 37 = hello from thread id # 15 at 2021-03-23T02:46:58.523996Z
output 38 = hello from thread id # 15 at 2021-03-23T02:46:58.524020Z
output 39 = hello from thread id # 15 at 2021-03-23T02:46:58.524049Z
output 40 = hello from thread id # 15 at 2021-03-23T02:46:58.524077Z
output 41 = hello from thread id # 15 at 2021-03-23T02:46:58.524102Z
output 42 = hello from thread id # 15 at 2021-03-23T02:46:58.524128Z
output 43 = hello from thread id # 15 at 2021-03-23T02:46:58.524156Z
output 44 = hello from thread id # 15 at 2021-03-23T02:46:58.524181Z
output 45 = hello from thread id # 15 at 2021-03-23T02:46:58.524212Z
output 46 = hello from thread id # 15 at 2021-03-23T02:46:58.524239Z
output 47 = hello from thread id # 15 at 2021-03-23T02:46:58.524262Z
output 48 = hello from thread id # 15 at 2021-03-23T02:46:58.524284Z
output 49 = hello from thread id # 15 at 2021-03-23T02:46:58.524308Z
output 50 = hello from thread id # 15 at 2021-03-23T02:46:58.524336Z
output 51 = hello from thread id # 15 at 2021-03-23T02:46:58.524359Z
output 52 = hello from thread id # 15 at 2021-03-23T02:46:58.524381Z
output 53 = hello from thread id # 15 at 2021-03-23T02:46:58.524405Z
output 54 = hello from thread id # 15 at 2021-03-23T02:46:58.524428Z
output 55 = hello from thread id # 15 at 2021-03-23T02:46:58.524454Z
output 56 = hello from thread id # 15 at 2021-03-23T02:46:58.524477Z
output 57 = hello from thread id # 15 at 2021-03-23T02:46:58.524499Z
output 58 = hello from thread id # 15 at 2021-03-23T02:46:58.524521Z
output 59 = hello from thread id # 15 at 2021-03-23T02:46:58.524544Z
output 60 = hello from thread id # 15 at 2021-03-23T02:46:58.524570Z
output 61 = hello from thread id # 15 at 2021-03-23T02:46:58.524591Z
output 62 = hello from thread id # 15 at 2021-03-23T02:46:58.524613Z
output 63 = hello from thread id # 15 at 2021-03-23T02:46:58.524634Z
output 64 = hello from thread id # 15 at 2021-03-23T02:46:58.524659Z
output 65 = hello from thread id # 15 at 2021-03-23T02:46:58.524685Z
output 66 = hello from thread id # 15 at 2021-03-23T02:46:58.524710Z
output 67 = hello from thread id # 15 at 2021-03-23T02:46:58.524731Z
output 68 = hello from thread id # 15 at 2021-03-23T02:46:58.524752Z
output 69 = hello from thread id # 15 at 2021-03-23T02:46:58.524780Z
output 70 = hello from thread id # 15 at 2021-03-23T02:46:58.524801Z
output 71 = hello from thread id # 15 at 2021-03-23T02:46:58.524826Z
output 72 = hello from thread id # 14 at 2021-03-23T02:46:58.524852Z
output 73 = hello from thread id # 14 at 2021-03-23T02:46:58.524902Z
output 74 = hello from thread id # 14 at 2021-03-23T02:46:58.524929Z
output 75 = hello from thread id # 14 at 2021-03-23T02:46:58.524954Z
output 76 = hello from thread id # 14 at 2021-03-23T02:46:58.524975Z
output 77 = hello from thread id # 14 at 2021-03-23T02:46:58.524998Z
output 78 = hello from thread id # 14 at 2021-03-23T02:46:58.525021Z
output 79 = hello from thread id # 14 at 2021-03-23T02:46:58.525042Z
output 80 = hello from thread id # 14 at 2021-03-23T02:46:58.525075Z
output 81 = hello from thread id # 14 at 2021-03-23T02:46:58.525095Z
output 82 = hello from thread id # 14 at 2021-03-23T02:46:58.525115Z
output 83 = hello from thread id # 14 at 2021-03-23T02:46:58.525138Z
output 84 = hello from thread id # 14 at 2021-03-23T02:46:58.525159Z
output 85 = hello from thread id # 14 at 2021-03-23T02:46:58.525194Z
output 86 = hello from thread id # 14 at 2021-03-23T02:46:58.525215Z
output 87 = hello from thread id # 14 at 2021-03-23T02:46:58.525241Z
output 88 = hello from thread id # 14 at 2021-03-23T02:46:58.525277Z
output 89 = hello from thread id # 14 at 2021-03-23T02:46:58.525298Z
output 90 = hello from thread id # 14 at 2021-03-23T02:46:58.525319Z
output 91 = hello from thread id # 14 at 2021-03-23T02:46:58.525339Z
output 92 = hello from thread id # 14 at 2021-03-23T02:46:58.525359Z
output 93 = hello from thread id # 14 at 2021-03-23T02:46:58.525381Z
output 94 = hello from thread id # 14 at 2021-03-23T02:46:58.525401Z
output 95 = hello from thread id # 14 at 2021-03-23T02:46:58.525422Z
output 96 = hello from thread id # 14 at 2021-03-23T02:46:58.525452Z
output 97 = hello from thread id # 14 at 2021-03-23T02:46:58.525474Z
output 98 = hello from thread id # 14 at 2021-03-23T02:46:58.525496Z
output 99 = hello from thread id # 14 at 2021-03-23T02:46:58.525515Z
output 100 = hello from thread id # 14 at 2021-03-23T02:46:58.525533Z
output 101 = hello from thread id # 14 at 2021-03-23T02:46:58.525555Z
output 102 = hello from thread id # 14 at 2021-03-23T02:46:58.525581Z
output 103 = hello from thread id # 14 at 2021-03-23T02:46:58.525603Z
output 104 = hello from thread id # 14 at 2021-03-23T02:46:58.525625Z
output 105 = hello from thread id # 14 at 2021-03-23T02:46:58.525645Z
output 106 = hello from thread id # 14 at 2021-03-23T02:46:58.525664Z
output 107 = hello from thread id # 14 at 2021-03-23T02:46:58.525686Z
output 108 = hello from thread id # 14 at 2021-03-23T02:46:58.525705Z
output 109 = hello from thread id # 14 at 2021-03-23T02:46:58.525723Z
output 110 = hello from thread id # 14 at 2021-03-23T02:46:58.525741Z
output 111 = hello from thread id # 14 at 2021-03-23T02:46:58.525758Z
output 112 = hello from thread id # 14 at 2021-03-23T02:46:58.525783Z
output 113 = hello from thread id # 14 at 2021-03-23T02:46:58.525801Z
output 114 = hello from thread id # 14 at 2021-03-23T02:46:58.525818Z
output 115 = hello from thread id # 14 at 2021-03-23T02:46:58.525837Z
output 116 = hello from thread id # 14 at 2021-03-23T02:46:58.525855Z
output 117 = hello from thread id # 14 at 2021-03-23T02:46:58.525875Z
output 118 = hello from thread id # 14 at 2021-03-23T02:46:58.525897Z
output 119 = hello from thread id # 14 at 2021-03-23T02:46:58.525913Z
output 120 = hello from thread id # 15 at 2021-03-23T02:46:58.525931Z
output 121 = hello from thread id # 15 at 2021-03-23T02:46:58.525965Z
output 122 = hello from thread id # 15 at 2021-03-23T02:46:58.526002Z
output 123 = hello from thread id # 15 at 2021-03-23T02:46:58.526023Z
output 124 = hello from thread id # 15 at 2021-03-23T02:46:58.526050Z
output 125 = hello from thread id # 15 at 2021-03-23T02:46:58.526075Z
output 126 = hello from thread id # 15 at 2021-03-23T02:46:58.526095Z
output 127 = hello from thread id # 15 at 2021-03-23T02:46:58.526135Z
output 128 = hello from thread id # 15 at 2021-03-23T02:46:58.526169Z
output 129 = hello from thread id # 15 at 2021-03-23T02:46:58.526233Z
output 130 = hello from thread id # 15 at 2021-03-23T02:46:58.526260Z
output 131 = hello from thread id # 15 at 2021-03-23T02:46:58.526279Z
output 132 = hello from thread id # 15 at 2021-03-23T02:46:58.526297Z
output 133 = hello from thread id # 15 at 2021-03-23T02:46:58.526315Z
output 134 = hello from thread id # 15 at 2021-03-23T02:46:58.526335Z
output 135 = hello from thread id # 15 at 2021-03-23T02:46:58.526352Z
output 136 = hello from thread id # 15 at 2021-03-23T02:46:58.526370Z
output 137 = hello from thread id # 15 at 2021-03-23T02:46:58.526389Z
output 138 = hello from thread id # 15 at 2021-03-23T02:46:58.526405Z
output 139 = hello from thread id # 15 at 2021-03-23T02:46:58.526424Z
output 140 = hello from thread id # 15 at 2021-03-23T02:46:58.526441Z
output 141 = hello from thread id # 14 at 2021-03-23T02:46:58.526465Z
output 142 = hello from thread id # 14 at 2021-03-23T02:46:58.526500Z
output 143 = hello from thread id # 14 at 2021-03-23T02:46:58.526524Z
output 144 = hello from thread id # 14 at 2021-03-23T02:46:58.526552Z
output 145 = hello from thread id # 14 at 2021-03-23T02:46:58.526570Z
output 146 = hello from thread id # 14 at 2021-03-23T02:46:58.526588Z
output 147 = hello from thread id # 14 at 2021-03-23T02:46:58.526605Z
output 148 = hello from thread id # 14 at 2021-03-23T02:46:58.526621Z
output 149 = hello from thread id # 14 at 2021-03-23T02:46:58.526642Z
output 150 = hello from thread id # 14 at 2021-03-23T02:46:58.526658Z
output 151 = hello from thread id # 14 at 2021-03-23T02:46:58.526674Z
output 152 = hello from thread id # 14 at 2021-03-23T02:46:58.526696Z
output 153 = hello from thread id # 15 at 2021-03-23T02:46:58.526715Z
您的期望似乎是线程调度程序将为每个线程提供相等的、循环的、活跃的时间片。
然而,Java 语言在线程调度方面不提供任何保证,而是将该责任留给了底层操作系统。如果您的应用程序中的线程没有执行时间敏感的工作,您可以将其作为实现细节归档,并假设调度程序将尝试为每个线程公平分配 运行 时间(当然假设线程逻辑本身不会饿死其他可运行的线程)。
另请注意,您可以 assign a priority 线程,但同样,这不提供任何执行顺序保证,只是声明您的意图,因为调度最终由底层 OS.
Java 线程调度算法是决定哪个线程应该 运行ning 在任何给定时间点的算法。而这个算法只关心Runnable state.
中的线程线程优先级的作用来了。 Java 线程可以使用 1 到 10 之间的整数值作为其优先级。您可以通过 threadName.setPriority(value).
设置线程的优先级如果不指定,thread默认取优先级值5。 因此,在您的情况下,两个线程都具有相同的优先级值 5。
关于java调度规则,在任何给定的时间点,最高优先级线程是运行ning。但是由于一些原因不能保证这条规则(例如:JVM 尽量避免 starvation)。
现在在这里,因为你有 2 个相同优先级的线程,Java 线程调度程序随机 select 一个线程(假设 t1)并执行。当以下情况之一发生时,另一个线程(t2)将有机会被执行,
- t1 完成 t1 的 运行 方法中的任何内容并再次竞争执行。但你不能保证下一次机会是 t2 的。 (这就是你的情况)
- 你调用了t1.yield()方法。 t1 放弃处理器并给任何其他相同优先级的线程机会。好吧,t2。 (但不能总是保证)
- Preempted 优先级更高的线程。
- 系统正在使用 Time Slicing Rule 并且特定时间到期。 (同样,不能保证 t2 会是下一个)。
如果你真的想在这里获得想要的0,1,0,1“公平”输出,你可以在Messenger中的synchronized方法中写一个逻辑class。像这样;
class Messenger {
private String msg = "hello";
private boolean state; //define a state variable
synchronized public void sendMessage() {
while(state == false){
if(!Thread.currentThread().getName().equals("Thread-0")){ //if not thread-0, then go to wait queue
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else{//if thread-0 , print and change state variable value.
System.out.println(msg + " from " + Thread.currentThread().getName());
state = true;
notifyAll();
}
}
if(!Thread.currentThread().getName().equals("Thread-1")){//if not thread-1, then go to wait queue
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else{//if thread-1 , print and change state variable value.
System.out.println(msg + " from " + Thread.currentThread().getName());
state = false;
notifyAll();
}
}
}