2015-07-19 90 views
2
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)等待获取锁定。但为什么 ?

+1

它们不会同时启动,因为您没有提供任何机制让它们同时启动。 –

+3

备注:请不要将字符串文字用作* locks *。如果你有多个类的代码,你的*性能工程师会有一个名为“程序太慢”的sev-1缺陷。接下来,你的预期输出是什么? – TheLostMind

+2

另外,在每个线程启动之后对'lock'变量的赋值基本上会使'synchronized(B.lock)'结构失效。当线程1进入同步块时,它拥有字符串“a”上的监视器。但是,您随后用“b”覆盖锁定,因此当线程2进入同步块时,它会获取“b”上的监视器,并且能够立即进入该块。你为什么要改变'lock'变量的值? – schtever

回答

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){ 
      //... 
     } 
    } 
} 

线程将现在都试图在同一时间进入​​块。显然,只有其中一个会在任何时候执行该块。

+0

如果OP要求不是单独启动,而是同时运行所有线程,那么我建议在'synchronized'块中添加一个'MyThread'属性作为锁。 – Turing85

+0

他可以简单地在'latch'上同步,不是吗?除了标题以外,我看不到他问任何问题。 –

+0

@AndyTurner我相信OP所要求的是为什么所有的线程都不能同时启动。如果你运行这个程序,你会注意到并不是所有的线程都立即开始。 (在我的机器上,任何时候只能同时启动两个线程,其他两个线程始终等待前两个线程才能启动) – CKing

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()不能保证是采取main下一步骤之前开始run方法。到控制转移到run()时,lock可能已经更改了两次值。操作本来可以重新排序。

Thread.start()真的不说,该线程将立即启动 - 它需要被安排和框架需要为run方法有所准备,到那个时候,事情在其他线程可能会发生的事情 - 像字符串被分配,创建的其他线程等。

你几乎可以保证线程1不会锁定在"a"上,因此会出现一些锁定比赛。

+0

请注意,我尝试了相同的测试,但在我的情况下,所有线程都锁定在“d”上,但尚未同时启动。这就是为什么我没有把这个解释作为答案。所有线程都锁定在“d”上,但只有两个线程可以同时运行?有一件事是肯定的,这个程序允许两个线程在大多数机器上同时运行。为什么锁的价值相同时,“d”超出了我的理解范围。 – CKing

+0

@CKing,如果他们锁定在同一个锁上,他们如何同时启动(如在,打印第一条消息),并在他们获得锁后打印?请注意,如果在锁之前添加了另一个'println()',那么您一直在同步'System.out'上的线程。 – RealSkeptic

+0

@CKing在进入'synchronized'块之前是否在局部变量中存储'B.lock'? – Turing85

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 lockMyThread并通过构造函数设置。

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使用。

相关问题