【问题标题】:Does using synchronized makes this code sequential?使用同步是否使此代码连续?
【发布时间】:2020-03-05 21:40:17
【问题描述】:

我是线程池的新手,正在学习使用synchronized

此代码存在竞争条件问题:

import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit ;
public class Counter implements Runnable{
    int count;
    public Counter(){
        count=0;
    }
    public void run(){
        count++;
    }
    public static void main(String[] args) throws 
    InterruptedException{
        ExecutorService exec=Executors.newFixedThreadPool(2);
        Counter task=new Counter();
        for (int i=0;i<1000;i++ ) {
            exec.execute(task); 
        }
        exec.shutdown();
        exec.awaitTermination(50L,TimeUnit.SECONDS);
        System.out.println(task.count);
    }
}

在此代码中,竞争条件被取消:

import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit ;
public class Counter implements Runnable{
    int count;
    public Counter(){
        count=0;
    }
    public synchronized void run(){
        count++;
    }
    public static void main(String[] args) throws 
    InterruptedException{
        ExecutorService exec=Executors.newFixedThreadPool(2);
        Counter task=new Counter();
        for (int i=0;i<1000;i++ ) {
            exec.execute(task); 
        }
        exec.shutdown();
        exec.awaitTermination(50L,TimeUnit.SECONDS);
        System.out.println(task.count);
    }
}

但我认为在第二个实现中使用线程是没有意义的,因为执行将是“某种”顺序​​的。因为两个线程中只有一个线程可以访问对象监视器,而另一个线程将等到第一个线程的执行,并且只有在第一个线程完成后才会成为监视器的访问权限。这听起来像是顺序的。

如果我错了,请纠正我。非常感谢任何帮助。

【问题讨论】:

标签: java multithreading threadpool executorservice synchronized


【解决方案1】:

使 run() 方法同步是在取消多线程。当然会导致顺序处理

请看Should you synchronize the run method? Why or why not?

【讨论】:

  • 好链接谢谢
【解决方案2】:

是的。非静态方法上的 synchronized 关键字将给定方法限制为每个实例的顺序执行。您只有一个 Counter 实例,并且正在为所有任务重用它,因此,即使您有一个包含 2 个线程的线程池,在任何给定时间只有一个会执行 run()。

【讨论】:

    【解决方案3】:

    首先,上面提供的代码只是为了学习用例,让学习者知道在使用多线程时可能会发生竞态条件,同时如何处理竞态条件。在上面提供的代码中,由于 synchronized 关键字使只有一个线程可以访问块(临界区),它确实是顺序的。考虑到上下文切换时间可以忽略不计和公平调度,多线程版本的运行时间会大于单线程版本使用的时间。

    但是退后一步。我们可以从多线程中获得的好处是我们可以同时执行程序的多个部分。因此,多线程通过多任务处理可以最大限度地利用 CPU。所以实际上我们需要考虑两种情况。一种是 CPU 繁重的程序(例如,您的代码中的示例)。另一个是具有大量 I/O 或网络绑定处理的程序(在 Web 应用程序中很常见)。

    第二个会很有趣,因为整个过程都没有发生在 CPU 中。

    例如,我们有

    task1 : 2 秒从 I/O + 2 秒 CPU 请求一些数据

    task2 : 2 秒从 I/O + 2 秒 CPU 请求一些数据

    如果我们在一个线程中运行,那么总时间将是 2s + 2s + 2s + 2s = 8 s。 但是如果我们在两个线程中运行,并且我们假设线程上下文切换会花费1s(实际时间很快,我们可以忽略)

    开始 -> task1 I/O 请求在 0

    -> task1 块和线程上下文更改为 task2 和 taks2 在 1s 开始发出 I/O 请求

    -> task2 块和线程上下文更改为 task1 和 task1 在 2s 从 IO 获取所有数据

    -> task2 4s 完成 CPU 操作

    -> 线程上下文在 5s 时更改为 task2

    -> task2 在 7s 完成 CPU。

    ->结束

    考虑 1 秒上下文切换时间和 3 次上下文切换(3 秒)= 7 秒。这仍然比单线程程序好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-25
      • 1970-01-01
      • 2018-10-09
      相关资源
      最近更新 更多