【问题标题】:java synchronization with executor frameworkjava与执行器框架的同步
【发布时间】:2015-03-17 18:44:21
【问题描述】:

这段代码有什么问题?

public class SimpleThreadPool {
    public static void main(String[] args) {  
        ExecutorService executor = Executors.newFixedThreadPool(5);  
        for (int i = 0; i < 2000; i++) {  
            Runnable worker = new WorkerThread("" + i);  
            executor.execute(worker);  
          }
        WorkerThread obj = new WorkerThread();
        System.out.println(obj.getCount());
        executor.shutdown();  
        while (!executor.isTerminated()) {  
        }  
        System.out.println("Finished all threads");  
    }  
}

class WorkerThread implements Runnable {  
    private String command; 
    private volatile static int count;
    public WorkerThread(){
    }
    public WorkerThread(String s){  
        this.command=s;  
    }  

    @Override 
    public void run() {  
        try {
            synchronized (this) {
                processCommand();
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }  

    private void processCommand() throws InterruptedException {
        count++;
        System.out.println("count " + count);
        //Thread.sleep(500);    
    }  

    @Override 
    public String toString(){  
        return this.command;  
    }

    public int getCount(){
        return count;
    }
} 

我试图得到 count as 2000 的结果,最后也是如此......我的代码有什么问题??
如何确保计数始终以正确的顺序递增?
我什么时候应该使用 volatile 或 synchronized 块或两者兼而有之?

【问题讨论】:

  • 您在循环中使用的是2000,而不是20000。你还没有告诉我们你的代码有什么问题,执行的实际结果是什么以及它与你想要的有什么不同。
  • 假设 processCommand() 应该做一些比增加计数器更重要的事情,那么在修复同步之后,原始设计仍然存在重大缺陷。 run() 中发生的第一件事是共享资源上的同步。在 Worker 应该完成的所有工作完成之前,不会释放该锁,这有效地消除了并行化带来的任何收益。 run 方法确实应该重新设计为同步、获取任何共享数据来执行此任务、发布、执行任务、同步、更新共享结果、发布。
  • 那么有什么方法可以在不同步 processCommand() 的情况下实现最终输出为 2000
  • 是的,正如我在上一条评论中提到的那样。您仅在访问共享资源的部分上进行同步。例如,如果 processCommand() 计算了一些应该添加到计数器的数字,则您仅在从计数器读取/写入时同步,因为这是线程之间共享的唯一数据集。如果您的最终结果确实是 int,您可以考虑查找 AtomicInteger

标签: java multithreading synchronized threadpoolexecutor


【解决方案1】:

您正在this 上进行同步。但是,当您这样做时,每个 WorkerThread 都会自己锁定,这没有效率。

processCommand 方法不是原子的,所以输出会乱序。此外,增量也不是原子的。它必须读取值、递增值并将值存储回变量中。

使count volatile 仅确保更新的值在写入后立即对其他线程可见,防止旧值在写入后被另一个线程读取。

如果您在所有线程上同步一个对象(WorkerThread.class 将在这里工作),那么每个Thread 都会更新并安全打印。

synchronized (WorkerThread.class) {
    processCommand();
}

如果你按照上面的方法正确同步,那么volatile在这里就没有必要了,虽然它不会伤害任何东西;对count 的访问受到完全控制。

注意:在将问题编辑为 2000 而不是 20000 之前,我已将其包含在内;它不再相关。

另外,要获得20000,您可能需要20000 个任务;它们每个只增加一次值。

for (int i = 0; i < 20000; i++) {  

【讨论】:

    【解决方案2】:

    您不能使用synchronized (this),因为这意味着您的所有工作人员都在同步不同的对象,即他们自己,这什么也没做。您需要它们在同一个对象上同步。

    解决问题的一种方法是在类上进行同步:

    synchronized (this.getClass()) {
        processCommand();
    }
    

    另请注意,无需同时使用 synchronizedvolatilesynchronized 块可确保您看到 count 的更新值。

    【讨论】:

      【解决方案3】:

      或者,您可以计算一个 AtomicInteger,并在其上使用 getAndIncrement()。

      【讨论】:

        【解决方案4】:
            public class IncrementThread implements Runnable{
            private static volatile int count;
            private String name;
            public IncrementThread(String name) {
            this.name = name;
            }
            public void run() {     
                synchronized (IncrementThread.class) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {              
                        e.printStackTrace();
                    }
                    //count = count + 1;
                    count++;
                    System.out.println("count is : "+count);
                }       
            }
            public int getCount() {
                return count;
            }   
        }
        
        
        import java.util.concurrent.ExecutorService;
        import java.util.concurrent.Executors;
        
        public class TestIncrementThread {
            public static void main(String[] args) {        
                ExecutorService executor = Executors.newFixedThreadPool(5);
                Runnable threadToRun = new IncrementThread("Increment");
                for(int i=0;i <2000; i++){
                    executor.execute(threadToRun);          
                }       
                IncrementThread it = new IncrementThread("Increment");      
                executor.shutdown();    
                if(executor.isTerminated())
                    System.out.println("Final count: "+it.getCount());
            }   
        }
        
        
        The above code will help you to achieve your goal.
        

        【讨论】:

        • 请评论您的代码。用户特别问:“怎么了?”。
        【解决方案5】:

        正如@Keppil 所说,您不能使用 synchronized (this) 。因为在这种情况下,每个线程都在不同的对象上工作,但根据您的要求,您需要在同一对象下增加变量。这就是我发现你的代码有问题的地方。请使用synchronized (WorkerThread.class) 来达到你想要的结果。这样,每个线程将仅以同步方式工作。是的,您可以根据您的要求删除 volatile 关键字。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-10-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-11
          相关资源
          最近更新 更多