同步关键字不起作用

时间:2015-11-07 22:08:15

标签: java multithreading synchronization

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()){}
}

3 个答案:

答案 0 :(得分:4)

在Task()中,你启动了100个线程(这很多),每个线程都要加1。

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

睡眠可能&#34;防止腐败&#34;只是因为它为系统提供了完成启动所有线程的时间。

除此之外,您正确使用同步。任何地方多个线程都可以写入你需要的同一个变量,而且一般来说(简化),如果你只是阅读,你就不需要它。

答案 1 :(得分:3)

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

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;
    }

}

首先,我们创建一个callables列表 - 一个将并行执行的函数列表。

然后我们在执行者服务上调用它们。 newCachedThreadPool我在这里使用过,默认情况下有0个线程,它会创建尽可能多的执行所有传递的callables,线程将在空闲一分钟后被杀死。

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

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

答案 2 :(得分: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);
    }
}