2017-10-20 74 views
4

我想了解java中的内部锁。我有一个程序,在这里我启动了2个线程,它们将在同一个对象上循环并调用同步的方法。我期望这两个线程并行执行,但看起来像是按顺序执行。java:为什么两个线程并行执行

如果我在环路中引入睡眠然后它们以随机顺序执行[如我预期]

public class Synchronized { 

    private int valueM; 

    public Synchronized(int value) { 
     valueM = value; 
    } 

    synchronized 
    public void one() throws InterruptedException 
    { 
     System.out.println("Object[" + valueM + "] executing one"); 
     Thread.sleep(100); //For case 2: comment it out 
     System.out.println("Object[" + valueM + "] completed one"); 
    } 

    synchronized 
    public void two() throws InterruptedException 
    { 
     System.out.println("Object[" + valueM + "] executing two"); 
     Thread.sleep(100); //For case 2: comment it out 
     System.out.println("Object[" + valueM + "] completed two"); 
    } 

} 

测试代码:

@org.junit.jupiter.api.Test 
    void test_sync() throws InterruptedException 
    { 
     Synchronized obj = new Synchronized(1); 

     Runnable task_one = new Runnable() { 
      public void run() { 
       for (int i=0 ; i<10; i++) 
       { 
        try { 
         obj.one(); 
         //Thread.sleep(100); //For case 2: uncomment it out 
        } catch (InterruptedException e) { 
         // TODO Auto-generated catch block 
         e.printStackTrace(); 
        } 
       } 
      } 
     }; 

     Runnable task_two = new Runnable() { 
      public void run() { 
       for (int i=0 ; i<10; i++) 
       { 
        try { 
         obj.two(); 
         //Thread.sleep(100); //For case 2: uncomment it out 
        } catch (InterruptedException e) { 
         // TODO Auto-generated catch block 
         e.printStackTrace(); 
        } 
       } 
      } 
     }; 

     Thread t1 = new Thread(task_one); 
     Thread t2 = new Thread(task_two); 

     t1.start(); 
     t2.start(); 

     t1.join(); 
     t2.join(); 
    } 

输出:

Case 1: output: 
Object[1] executing one 
Object[1] completed one 
...10times 
Object[1] executing two 
Object[1] completed two 
...10times 

Case 2: output: random order 
Object[1] executing one 
Object[1] completed one 
Object[1] executing two 
Object[1] completed two 
... 

UPDATE : 原来的问题是固定的..看起来它是随机的,即使在情况1也,但我看到它当我加载更多的迭代(30K)..

因此,在没有睡眠的for循环中线程切换发生得少得多? Java-JVM试图让for循环将其作为“种类”原子(尽可能不完全但尽可能?)来执行,这是否有点特殊?

+0

如果让环形变大,该怎么办?例如30k或更多的迭代? –

+0

@MargaretBloom我怀疑是这样,用1000试过,让我用30K做 –

+0

@SamDaniel我复制你的代码并按原样运行它:它不是为了顺序。它显示了9次1次,10次2次,1次1.我认为,如果没有睡眠,方法非常快,以至于run()调用之间的时间足够第一个线程完成,或者至少在第二个工作开始之前完成大部分工作。 –

回答

0

您已标记方法onetwo为​​。这意味着在一个线程可以输入任何一个线程之前,它必须获得obj的锁定。如果另一个线程持有线程,线程无法获取锁定。当线程退出one/two时,锁定被释放,两个线程再次竞争。有时第一个线程成功,有时第二个线程成功 - 这就是为什么你看到随机的调用顺序,但从来没有混淆过。

所以这是设计。实际上,你已经告诉JVM你不需要希望两个线程同时运行。

+0

我明白线程的概念。问题是为什么它在第一种情况下不是随机的。 –

+2

排序不确定。随着线程竞争,下一次可能会获得锁定。没有任何形式的保证。实际结果可能取决于JVM实现,CPU调度算法或缺乏更好的示例天气。所以即使你看到一个序列,实际上仍然是一个随机的(更确切地说:不可预知的)命令。 – jurez

1

intrinsic锁(​​关键字)被认为是“不公平的”,这意味着不能保证锁的获取率在竞争线程中是相同的。

这是一个已知的事实,释放锁的线程通常更有可能再次获取它导致您遇到的问题。

如果你希望你的线程也有类似的收购可能性(公平),你可以使用一个明确的锁像ReentrantLock确保使用可选boolean参数设置为true

ReentrantLock(boolean fair) 

然后你可以使用它这种方式

class X { 
    private final ReentrantLock lock = new ReentrantLock(true); 

    public void m() { 
    lock.lock(); 
    try { 
     // method body 
    } finally { 
    lock.unlock() 
    } 
    } 
} 
0

让我们尝试了解您的问题,然后尝试看到预期的结果。

  1. 有2种方法,其相同的对象(Synchronized类型的当前对象)上是​​。

  2. 有2个主题。其执行路径中的每个线程都会尝试多次调用其中一个同步方法。

  3. 有两种情况,情况1没有在线程上调用sleep方法,而情况2在当前执行的线程上调用sleep方法。

现在从第3个点开始。sleep不释放锁。它的wait方法在获得锁的对象上调用,释放其他线程的锁。所以在你的情况下,基本上睡眠只会让执行变得缓慢而没有别的。

线程调度程序决定线程的执行顺序以及处理器周期共享。它不保证任何顺序,也不保证任何随机性,它可能会或可能不是随机的。

现在当我说sleep没有释放锁,那么我们有时候如何得到随机的执行顺序?答案是:只要一个同步方法的执行被两个线程中的一个结束,锁就释放,线程调度程序决定哪个线程给下一个执行机会。

0

线程#开始是相对而言非常慢的方法。数到10(或数到1,000)不需要很长时间。第一个线程在操作系统完成第二个线程的实际执行之前就已经完成计数。如果你想要“同时”启动两个线程,你需要使用一个锁存器。

您的测试也受到以下事实的困扰:根据您的执行环境,系统控制台编写器本身可能是同步的竞争资源(或者相反,可能无法保证以时间点一致的方式刷新和写入顺序线程访问它。)尝试使用System.out.println调试并发问题已经引起了许多人多年来的麻烦,因为获取控制台编写器的暂停通常会隐藏它们的内存一致性错误。

public static CountDownLatch latch = new CountDownLatch(1); 

public static class Thing implements Runnable { 
    @Override 
    public void run() { 
     try { 
      latch.await(); 
      //doStuff 
     } catch (InterruptedException e) { 

     } 
    } 
} 
public static void main(String[] args) throws Exception { 

    Thing thing1 = new Thing(); 
    Thing thing2 = new Thing(); 
    new Thread(thing1).start(); 
    new Thread(thing2).start(); 
    latch.countDown(); 
} 
相关问题