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)等待获取锁定。但为什么呢?
答案 0 :(得分:2)
如果要同时启动所有线程,则需要提供一些机制,通过该机制,线程可以在开始工作之前知道其他线程已准备好执行。
像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){
//...
}
}
}
线程现在都会同时尝试进入synchronized
块。显然,其中只有一个会在任何时候执行该块。
答案 1 :(得分:2)
这个答案的灵感来自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
对象。
答案 2 :(得分:1)
一个教育提示是尝试在您的帖子中打印实际的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()
无法保证run
方法在main
的下一步开始之前启动run()
方法。到控件转移到lock
时,Thread.start()
可能会更改两次值。操作本可以重新排序。
run
实际上没有说线程会立即启动 - 它需要被调度,并且需要为"a"
方法准备一个框架,到那时,可能正在发生其他线程 - 比如分配字符串,创建其他线程等等。
你几乎可以保证线程1不会锁定在CREATE TEMPORARY TABLE TEMP (half TIME);
DROP PROCEDURE IF EXISTS insertTEMP;
DELIMITER //
CREATE PROCEDURE insertTEMP (first TIME, last TIME) begin
WHILE first <= last DO
INSERT INTO TEMP Values (first);
SET first = ADDTIME(first, "00:30");
END WHILE;
END //
DELIMITER ;
CALL insertTEMP("04:00", "12:00");
SELECT TEMP.half `Time`, COUNT( actions.name ) `count`, GROUP_CONCAT(actions.name) actions FROM (TEMP, actions) WHERE TEMP.half between actions.rowStarTime AND actions.rowEndTime GROUP BY half
,因此会有一些争夺锁定。