为什么不是所有线程都同时启动?
public class B {
public static String lock = "a";
public static void main(String[] args) {
MyThread t1 = new MyThread("Thread 1");
t1.start();
lock = "b";
MyThread t2 = new MyThread("Thread 2");
t2.start();
lock = "c";
MyThread t3 = new MyThread("Thread 3");
t3.start();
lock = "d";
MyThread t4 = new MyThread("Thread 4");
t4.start();
}
}
class MyThread extends Thread{
public MyThread(String name) {
super(name);
}
@Override
public void run() {
synchronized (B.lock){
System.out.println(Thread.currentThread().getName() +" is going to sleep for 5 seconds");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " done sleeping ");
}
}
}
输出::为什么不是所有线程都同时启动?
Thread 1 is going to sleep for 5 seconds
Thread 2 is going to sleep for 5 seconds
Thread 2 done sleeping
Thread 1 done sleeping
Thread 4 is going to sleep for 5 seconds
Thread 4 done sleeping
Thread 3 is going to sleep for 5 seconds
Thread 3 done sleeping
对不起,我不清楚的问题。但我的查询是因为我在每次线程启动后都更改锁对象,为什么不是所有线程都同时启动并锁定不同的字符串对象?我猜想可能是操作系统线程调度的原因。但每次执行都会导致仅同时启动2个线程(1 & 2),而其余2个线程(3 & 4)等待获取锁定。但为什么 ?
如果你想同时启动所有的线程,你需要提供一些机制,使一个线程可以知道其他线程准备执行它开始工作之前。
有点像java.util.concurrent.CountDownLatch
将有助于这一点。基本的想法是,你在线程中首先要做的是等待CountDownLatch
;当所有线程都被创建并启动时,您只能将锁定计数到零。
例如,在你的main
方法:
CountDownLatch latch = new CountDownLatch(4);
MyThread t1 = new MyThread("Thread 1", latch);
t1.start();
//...
MyThread t4 = new MyThread("Thread 4", latch);
t4.start();
在你MyThread
:
class MyThread extends Thread{
private final CountDownLatch latch;
public MyThread(String name, CountDownLatch latch) {
super(name);
this.latch = latch;
}
@Override
public void run() {
latch.countDown();
latch.await();
synchronized (B.lock){
//...
}
}
}
线程将现在都试图在同一时间进入块。显然,只有其中一个会在任何时候执行该块。
一个教育暗示将尝试在你的线程您的邮件打印的实际B.lock
在一起:
@Override
public void run() {
String currLock = B.lock;
synchronized (currLock){
System.out.println(Thread.currentThread().getName() +" is going to sleep for 5 seconds locking " + currLock);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " done sleeping ");
}
}
可能系统之间的不同,但我得到这样的输出:
Thread 1 is going to sleep for 5 seconds locking c Thread 3 is going to sleep for 5 seconds locking d Thread 1 done sleeping Thread 3 done sleeping Thread 2 is going to sleep for 5 seconds locking c Thread 4 is going to sleep for 5 seconds locking d Thread 4 done sleeping Thread 2 done sleeping
(好吧,有时我会得到一些不同的结果,但让我们看看我们在这里得到了什么)。
的时候线程1得锁上的锁,它已经改变了值的两倍。所以它锁定在"c"
。线程2看起来也不幸得到了"c"
,并且因为它已经被锁定,所以它正在等待而不是打印。
通过时间线3运行,该数值已经变为"d"
,这就是它的锁定。线程4也获得了该锁,所以它也被延迟。
因此,一些延迟可能是由于操作系统无法真正运行的线程在一起。但是,还有一部分是由于简单的事实,start()
不能保证是采取main
下一步骤之前开始run
方法。到控制转移到run()
时,lock
可能已经更改了两次值。操作本来可以重新排序。
Thread.start()
真的不说,该线程将立即启动 - 它需要被安排和框架需要为run
方法有所准备,到那个时候,事情在其他线程可能会发生的事情 - 像字符串被分配,创建的其他线程等。
你几乎可以保证线程1不会锁定在"a"
上,因此会出现一些锁定比赛。
请注意,我尝试了相同的测试,但在我的情况下,所有线程都锁定在“d”上,但尚未同时启动。这就是为什么我没有把这个解释作为答案。所有线程都锁定在“d”上,但只有两个线程可以同时运行?有一件事是肯定的,这个程序允许两个线程在大多数机器上同时运行。为什么锁的价值相同时,“d”超出了我的理解范围。 – CKing
@CKing,如果他们锁定在同一个锁上,他们如何同时启动(如在,打印第一条消息),并在他们获得锁后打印?请注意,如果在锁之前添加了另一个'println()',那么您一直在同步'System.out'上的线程。 – RealSkeptic
@CKing在进入'synchronized'块之前是否在局部变量中存储'B.lock'? – Turing85
此答案受Andy Turner's comment的启发。
论此事为什么程序的行为,因为它的作用:B.lock
是不是你认为它是值。我稍微修改了你的源代码。
public class B {
public static String lock = "a";
public static void main(String... args) {
lock = "a";
MyThread t1 = new MyThread("Thread 1");
t1.start();
lock = "b";
MyThread t2 = new MyThread("Thread 2");
t2.start();
lock = "c";
MyThread t3 = new MyThread("Thread 3");
t3.start();
lock = "d";
MyThread t4 = new MyThread("Thread 4");
t4.start();
// lock = "e";
}
}
class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
String s = B.lock;
synchronized (s) {
System.out.println( Thread.currentThread()
.getName()
+ " is going to sleep for 5 seconds");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( Thread.currentThread().getName()
+ " done sleeping ");
}
System.out.println(Thread.currentThread().getName() + ": " + s);
}
}
请理解,从这里开始了,我只能说明我的机器的行为,因为行为在很大程度上取决于你运行在主机系统上。但是,我提供的解决方案应该独立于主机系统。
如果我执行这个代码,我得到这样的一些输出:
线程2进入睡眠5秒
线3临睡前5秒
主题1要睡5秒
线程1完成睡眠
线程1:b
线程3完成睡眠
线程2完成睡眠
线程2:C
线程3:d
螺纹4临睡前5秒
螺纹4完成睡眠
线程4:d
所以,正如你所看到的,线程的开始稍微延迟了。此时,main
方法已经更改了lock
字段。因此最后一个线程起步较晚。
如果我对最后一行取消注释,程序按预期运行(但这绝不是保证)。
要解决这个问题,我添加了一个Object lock
到MyThread
并通过构造函数设置。
public class B {
public static String lock = "a";
public static void main(String... args) {
lock = "a";
MyThread t1 = new MyThread("Thread 1", lock);
t1.start();
lock = "b";
MyThread t2 = new MyThread("Thread 2", lock);
t2.start();
lock = "c";
MyThread t3 = new MyThread("Thread 3", lock);
t3.start();
lock = "d";
MyThread t4 = new MyThread("Thread 4", lock);
t4.start();
}
}
class MyThread extends Thread {
Object lock;
public MyThread(String name, Object lock) {
super(name);
this.lock = lock;
}
@Override
public void run() {
synchronized (lock) {
System.out.println( Thread.currentThread()
.getName()
+ " is going to sleep for 5 seconds");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( Thread.currentThread().getName()
+ " done sleeping ");
}
System.out.println(Thread.currentThread().getName() + ": " + lock);
}
}
这样,你去耦lock
从它的实际设置的获取和你有完全的控制上lock
对象中Thread
使用。
它们不会同时启动,因为您没有提供任何机制让它们同时启动。 –
备注:请不要将字符串文字用作* locks *。如果你有多个类的代码,你的*性能工程师会有一个名为“程序太慢”的sev-1缺陷。接下来,你的预期输出是什么? – TheLostMind
另外,在每个线程启动之后对'lock'变量的赋值基本上会使'synchronized(B.lock)'结构失效。当线程1进入同步块时,它拥有字符串“a”上的监视器。但是,您随后用“b”覆盖锁定,因此当线程2进入同步块时,它会获取“b”上的监视器,并且能够立即进入该块。你为什么要改变'lock'变量的值? – schtever