2015-11-07 129 views
2
package threadShareResource1; 

public class NonSynchro1 { 
    private int sum = 0; 

    public static void main(String[] args) { 
     NonSynchro1 n = new NonSynchro1(); 
     n.task(); 
     System.out.println(n.getSum()); 
    } 

    public synchronized void sumAddOne(){ 
     sum++; 
    } 

    public void task(){ 
     for (int i = 0; i < 100; i++) { 
      new Thread(new Runnable(){ 
       @Override 
       public void run() { 
        sumAddOne(); 
       } 
      }).start(); 

     /* try { 
       Thread.sleep(10); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } */ 
     } 
    } 
    public int getSum() { 
     return sum; 
    } 
} 

没有注释的代码部分,程序有数据损坏,每次运行它时都不是100。但我认为synchronized关键字应该获得对sumAddOne方法的锁定,该方法是我的程序的关键区域,允许一次线程访问此方法。同步关键字不起作用

我试着使用ExecutorService,但它并没有给出100个所有的运行。

public void task(){ 
    ExecutorService s = Executors.newCachedThreadPool(); 

    for (int i = 0; i < 100; i++) { 
     s.execute(new Thread(new Runnable(){ 
      @Override 
      public void run() { 
       sumAddOne(); 
      } 
     })); 
    } 
    s.shutdown(); 

    while(!s.isTerminated()){} 
} 
+0

预期行为是什么?你想达到什么目的? –

+0

测试何时使用同步访问公共变量的100个线程。所有的运行结果应该是100。 –

回答

4

在任务()中,您启动100个线程(这是很多),每个线程将添加1到总和。

但是,当完成任务时,您所知道的是100个线程正处于启动的某个过程中。你在调用println()之前不会阻塞,所以你怎么知道所有的线程都已经完成?

睡眠可能“防止腐败”,只是因为它让系统有时间完成启动所有线程。

除此之外,您正在使用正确同步。任何地方多个线程可能会写入您需要的相同变量,并且通常(简化),如果您只是阅读,则不需要它。

+0

你是完全正确的,当我使用'ExecutorService'时,我忘了使用'synchronized',这就是为什么结果不对。所以问题和你描述的一样,谢谢。 –

+0

你非常欢迎韦斯利。看起来这只是学习的演示代码,但为了防万一,我想指出100个线程很多,不应该用于生产代码。您可以轻松地排出机器的螺纹。一般来说,如果您需要更多的话,您可以使用1个或“几个”线程或移动到线程池:https://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html –

3

同步关键字使用正确,问题在于您没有等待线程完成。这是一个可能的解决方案:

public class NonSynchro1 { 

    private static final ExecutorService executorService = Executors.newCachedThreadPool(); 

    private int sum = 0; 

    public static void main(String[] args) { 
     NonSynchro1 n = new NonSynchro1(); 
     n.task(); 
     System.out.println(n.getSum()); 
     executorService.shutdown(); 
    } 

    public synchronized void sumAddOne() { 
     sum++; 
    } 

    public void task() { 
     List<Callable<Object>> callables = new ArrayList<>(); 
     for (int i = 0; i < 100; i++) { 
      callables.add(() -> { 
       sumAddOne(); 
       return null; 
      }); 
     } 

     List<Future<Object>> futures; 
     try { 
      futures = executorService.invokeAll(callables); 
     } catch (InterruptedException e) { 
      throw new RuntimeException(e); 
     } 

     futures.forEach(future -> { 
      try { 
       future.get(); 
      } catch (ExecutionException | InterruptedException e) { 
       throw new RuntimeException(e); 
      } 
     }); 
    } 

    public int getSum() { 
     return sum; 
    } 

} 

首先我们创建一个可调用列表 - 一个将并行执行的函数列表。

然后我们在执行器服务上调用它们。 newCachedThreadPool我已经在这里使用了,默认情况下有0个线程,它会创建尽可能多的执行所有传递的可调用对象,这些线程会在闲置一分钟后被终止。

最后,在for-each循环中我们解决了所有的问题。呼叫将阻塞,直到执行程序服务执行该功能。它也会抛出异常,如果它被抛入函数内(没有调用get()你根本看不到这种异常)。

此外,当您想要优雅地终止程序时关闭执行程序服务是一个好主意。在这种情况下,主方法结束时只是executorService.shutdown()。如果你不这样做,程序将在空闲线程死亡一分钟后终止。但是,如果不同的执行程序服务,线程在空闲时可能不会被终止,在这种情况下,程序永远不会终止。

+0

谢谢您的回答,我已经修好了 –

+1

很高兴你把它修好了Wesley,我只想指出,这家伙的策略非常好,因为它使用线程池而不是直接管理线程。 –

+0

你可能应该在'ExecutorService'上调用'shutdown' –

0

只是为了完整性的缘故:这里显示原来的程序如何,可向等待所有线程由joining他们完成一个解决方案:

 for (Thread t : n.task()) 
      try { 
       t.join(); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 

要求task回到它创建线程。在这种情况下,我们不需要使缓存管理器或集合变得复杂:一个简单的数组就可以。以下是完整的课程:

public class TestSynchro1 { 
    private int sum = 0; 

    public synchronized void sumAddOne() { 
     sum++; 
    } 

    public Thread[] task(int n) { 
     Thread[] threads = new Thread[n]; 
     for (int i = 0; i < n; i++) { 
      (threads[i] = new Thread(new Runnable() { 
       @Override 
       public void run() { 
        sumAddOne(); 
       } 
      })).start(); 
     } 
     return threads; 
    } 

    public static void main(String[] args) { 
     TestSynchro1 n = new TestSynchro1(); 
     for (Thread t : n.task(100)) 
      try { 
       t.join(); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     System.out.println(n.sum); 
    } 
}