Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 它是线程安全机制吗?_Java_Multithreading_Thread Safety - Fatal编程技术网

Java 它是线程安全机制吗?

Java 它是线程安全机制吗?,java,multithreading,thread-safety,Java,Multithreading,Thread Safety,这个类是线程安全的吗 class Counter { private ConcurrentMap<String, AtomicLong> map = new ConcurrentHashMap<String, AtomicLong>(); public long add(String name) { if (this.map.get(name) == null) { this.map.putIfAbsent(name, new Ato

这个类是线程安全的吗

class Counter {
  private ConcurrentMap<String, AtomicLong> map = 
    new ConcurrentHashMap<String, AtomicLong>();
  public long add(String name) {
    if (this.map.get(name) == null) {
      this.map.putIfAbsent(name, new AtomicLong());
    }
    return this.map.get(name).incrementAndGet();
  }
}
类计数器{
私有ConcurrentMap=
新的ConcurrentHashMap();
公共长添加(字符串名称){
if(this.map.get(name)==null){
this.map.putIfAbsent(名称,new AtomicLong());
}
返回此.map.get(name).incrementAndGet();
}
}
你觉得怎么样?

编辑

如果使地图成为最终地图,则为“是”。否则,不能保证所有线程在第一次调用
add()
时都能看到最新版本的map数据结构

几个线程可以到达
if()
的主体。
putIfAbsent()
将确保仅将单个
AtomicLong
放入映射

如果映射中没有新值,
putIfAbsent()
将无法返回

因此,当执行第二个
get()
时,它将永远不会获得
null
值,并且由于只能将单个
AtomicLong
添加到映射中,因此所有线程将获得相同的实例

[EDIT2]下一个问题:这有多高效

此代码速度更快,因为它避免了不必要的搜索:

public long add(String name) {
    AtomicLong counter = map.get( name );
    if( null == counter ) {
        map.putIfAbsent( name, new AtomicLong() );
        counter = map.get( name ); // Have to get again!!!
    }
    return counter.incrementAndGet();
}

这就是为什么我更喜欢谷歌的,它有一个在找不到密钥时调用的方法。这样,地图只会被搜索一次,我不必创建额外的实例。

是的,前提是您将地图设置为最终地图。if不是必需的,但是如果您愿意,可以出于性能原因保留它,尽管它很可能不会产生明显的差异:

public long add(String name) {
  this.map.putIfAbsent(name, new AtomicLong());
  return this.map.get(name).incrementAndGet();
}
编辑

为此,我快速测试了这两种实现(有检查和没有检查)。在同一字符串上拨打1000万次电话:

  • 250毫秒和支票
  • 480毫秒,无需检查
这证实了我所说的:除非您多次调用这个方法,或者它是代码中性能关键的部分,否则它不会有什么区别

编辑2

完整测试结果-请参阅产生更好结果的更好计数器。现在,测试非常具体(无争用+get始终有效),并且不一定符合您的使用

计数器:482毫秒
懒散计数器:207毫秒
MPC计数器:303毫秒
最佳计数器:135毫秒

公共类测试{
公共静态void main(字符串args[])引发IOException{
计数器计数=新计数器();
LazyCounter lazyCount=新的LazyCounter();
MPCounter mpCount=新的MPCounter();
BetterCounter betterCount=新的BetterCounter();
//热身
对于(int i=0;i<10_000;i++){
计数。添加(“abc”);
lazyCount.add(“abc”);
mpCount.添加(“abc”);
更好的计算添加(“abc”);
}
//试验
长启动=System.nanoTime();
对于(int i=0;i<10_000;i++){
计数。添加(“abc”);
}
long end=System.nanoTime();
System.out.println((结束-开始)/1000000);
start=System.nanoTime();
对于(int i=0;i<10_000;i++){
lazyCount.add(“abc”);
}
end=System.nanoTime();
System.out.println((结束-开始)/1000000);
start=System.nanoTime();
对于(int i=0;i<10_000;i++){
mpCount.添加(“abc”);
}
end=System.nanoTime();
System.out.println((结束-开始)/1000000);
start=System.nanoTime();
对于(int i=0;i<10_000;i++){
更好的计算添加(“abc”);
}
end=System.nanoTime();
System.out.println((结束-开始)/1000000);
}
静态类计数器{
私有最终ConcurrentMap映射=
新的ConcurrentHashMap();
公共长添加(字符串名称){
this.map.putIfAbsent(名称,new AtomicLong());
返回此.map.get(name).incrementAndGet();
}
}
静态类懒散计数器{
私有最终ConcurrentMap映射=
新的ConcurrentHashMap();
公共长添加(字符串名称){
if(this.map.get(name)==null){
this.map.putIfAbsent(名称,new AtomicLong());
}
返回此.map.get(name).incrementAndGet();
}
}
静态类BetterCounter{
私有最终ConcurrentMap映射=
新的ConcurrentHashMap();
公共长添加(字符串名称){
AtomicLong计数器=this.map.get(名称);
如果(计数器!=null)
返回计数器.incrementAndGet();
AtomicLong newCounter=新的AtomicLong();
计数器=this.map.putIfAbsent(名称,newCounter);
return(counter==null?newCounter.incrementAndGet():counter.incrementAndGet());
}
}
静态类计数器{
私有最终ConcurrentMap映射=
新的ConcurrentHashMap();
公共长添加(字符串名称){
final AtomicLong newVal=新的AtomicLong(),
prevVal=map.putIfAbsent(名称,newVal);
return(prevVal!=null?prevVal:newVal).incrementAndGet();
}
}
}
这个怎么办:

class Counter {

  private final ConcurrentMap<String, AtomicLong> map = 
    new ConcurrentHashMap<String, AtomicLong>();

  public long add(String name) {
    this.map.putIfAbsent(name, new AtomicLong());
    return this.map.get(name).incrementAndGet();
  }
}
类计数器{
私有最终ConcurrentMap映射=
新的ConcurrentHashMap();
公共长添加(字符串名称){
this.map.putIfAbsent(名称,new AtomicLong());
返回此.map.get(name).incrementAndGet();
}
}
  • map
    应该是
    final
    ,以确保在调用第一个方法之前,它对所有线程都是完全可见的。(详情请参阅)
  • 我认为
    如果
    是多余的,我希望我没有监督任何事情

编辑:添加了Java语言规范中的一段引语:

我认为您最好使用以下内容:

class Counter { 
  private ConcurrentMap<String, AtomicLong> map = new ConcurrentHashMap<String, AtomicLong>();

  public long add(String name) {
    AtomicLong counter = this.map.get(name);
    if (counter == null) {
      AtomicLong newCounter = new AtomicLong();
      counter = this.map.putIfAbsent(name, newCounter);
      if (counter == null) {
        // The new counter was added - use it
        counter = newCounter;
      }
    }

    return counter.incrementAndGet();
  }
}
类计数器{
脉波重复间隔
class Counter { 
  private ConcurrentMap<String, AtomicLong> map = new ConcurrentHashMap<String, AtomicLong>();

  public long add(String name) {
    AtomicLong counter = this.map.get(name);
    if (counter == null) {
      AtomicLong newCounter = new AtomicLong();
      counter = this.map.putIfAbsent(name, newCounter);
      if (counter == null) {
        // The new counter was added - use it
        counter = newCounter;
      }
    }

    return counter.incrementAndGet();
  }
}
final AtomicLong newVal = new AtomicLong(), 
                 prevVal = map.putIfAbsent(name, newVal);
return (prevVal != null? prevVal : newVal).incrementAndGet();
  public long add(String name) {
    AtomicLong counter = this.map.get(name);
    if (counter == null) {
      AtomicLong newCounter = new AtomicLong();
      counter = this.map.putIfAbsent(name, newCounter);
      if(counter == null) {
        counter = newCounter;
      }
    }

    return counter.incrementAndGet();
  }