线程安全的统计工具(计数器)

利用Java中的AtomicInteger实现高并发环境下的线程安全计数器 AtomicInteger`是Java并发包中一个非常实用的工具类,它通过硬件级别的CAS指令,为开发者提供了一种高效、线程安全的整数更新方案。在实现高并发计数器这类需求时,它往往是优于传统同步锁的首选。然而,开发者也需要清楚其适用场景和潜在限制,对于更复杂的并发控制,可能需要结合使用`ConcurrentHashMap`、`LongAdder`(适用于高并发写多读少的求和场景)等其他并发组件来构建健壮的高性能应用。 阅读详情

问题描述

    我们需要一个统计工具来统计诸如查询次数,最短和最长的查询时间,另外还需要按类别来统计,比如按城市统计查询数量等等。

    我们需要它是线程安全的,因为我们会有很多线程同时更新统计数据。

简单的方案

    最简单的方案是不保证线程安全,因为既然是统计数据,那么差一点点也许没有问题。

    其次比较简单的翻案就是用synchronized保护所有的数据,这是首先推荐的做法,如果发现有性能问题,再看下面更加复杂的方案。

高效但复杂的方案

    我们这里就假设需要非常高效并且线程安全的方案(比如需要追求极致性能或者你是性能优化狂)。

 

问题1 统计查询次数 

    这个比较简单,使用AtomicInteger或者AtomicLong就可以了。比如:

      privatefinal AtomicLong totalQuery=new AtomicLong();

        public void addTotalQuery(){

            totalQuery.incrementAndGet();

      }


 

问题2 统计最短查询时间

     首先看不做任何同步会怎么样?

       private long minTime=Long.MAX_VALUE;

       public void updateMinTime(long time){

           if(time<minTime){

                minTime=time;

           }

       }

       public long getMinTime(){

           return minTime;

       }


 

     比如minTime的当前值是5,然后线程1的time是3,线程2的time是4,他们同时发现自己比5小,所以都会试图把自己的值赋给minTime。

     如果线程1先赋值,线程2后赋值,那么最终的值将是4,这样就记错了。

     除了上面的问题,这个方法还有个bug,JVM对long的赋值不是原子的,尤其是对于32位的JVM,很可能一个long的赋值先修改高32位,然后修改低32位,那么如果

     在这直接有人读取这个值,那么就会读到奇怪的结果。

     另外即使没有人读,两个线程同时写一个long也会出现一个线程更新了高32位,另一个修改了低32位的情况。解决原子修改的问题可以使用volatile或者AtomicLong都可以,但是即使改成了volatile或者AtomicLong,仍然不能解决第一个带来的race condition。

     那么怎么解决呢?当然加锁是一种方案

       @Guarded by this
       private long minTime=Long.MAX_VALUE;
       public void synchronized updateMinTime(long time){
           if(time<minTime){
                minTime=time;
           }
       }
       public void synchronized getMinTime(){
           return minTime;
       }

     注意:不但更新需要synchronized,读取也需要synchronized。因为synchronized除了互斥的语义之外,还定义了happens-before的关系,它保证updateMinTime的结果完成后才会执行getMinTime,而且和volatile一样,它保证了可见性。

    我们可以用volatile或者AtomicLong稍微优化一下:


       @Guarded by this
       private volatile long minTime=Long.MAX_VALUE;
       public void synchronized updateMinTime(long time){
           if(time<minTime){
                minTime=time;
           }
       }
       public void getMinTime(){
           return minTime;
       }

      我们用synchronized来保证只有一个线程能够修改minTime,而且由于它是volatile的,所以getMinTime不会读到更新long一半的情况。

      但这样只能有一个线程修改minTime,还是不是特别高效。

      我们能够发现:其实大部分线程的值都不是最小的值,它发现自己的值可能比minTime大,那么根本不需要更新。那能不能下面这样优化呢?

       @Guarded by this
       private volatile long minTime=Long.MAX_VALUE;
       public void updateMinTime(long time){
           if(time<minTime){             
               synchronized(this){
                   minTime=time;
               }
           }
       }
       public void getMinTime(){
           return minTime;
       }

       还是不行,因为还是可能线程1和线程2同时发现自己比较小,然后都试图更新,依然会出现上面的问题。

        当然如果大家碰到过Lazy Initialization的问题可能会知道可以使用类似Double Check Lock的方法来优化。

       @Guarded by this
       private volatile long minTime=Long.MAX_VALUE;
       public void updateMinTime(long time){
           if(time<minTime){
               synchronized(this){
                  if(time<minTime){//check again
                       minTime=time;
                  }
               }
           }
       }
       public void getMinTime(){
           return minTime;
       }

          这种使用double check lock的方法配合volatile的方案是没有问题的。但是明显实现起来很复杂。

          我们再来仔细分析它为什么正确。

          如果线程1和线程2同时发现自己比当前的minTime小,那么只有一个线程能进入synchronized代码段,这个时候再检查一下自己的值是否小于当前值。

          如果当前值比自己小,那么说明在第一次判断和拿到锁这段时间内有别人修改了最小值,而且还比自己小,那么自己就没有比更新最小值了。

          否则自己还是最小值,而且由于已经拿到锁,所以不会再有人能修改最小值,所以可以放心的更新最小值。

          它比在外层加锁的好处是:大部分时候第一个if就发现自己不是最小值了,那么更本不需要加锁,所以很快。

           我们总结一下它正确的原因:那就是它保证了条件判断(if(time<minTime))和修改(minTime=time;)是原子的,这个是通过synchronized来实现的。

           除了synchronized之外,很多现代处理器都提供了一些原子的指令,比如CAS。我们可以使用这些原子的指令来实现同样的功能。

           当然Java是跨平台的实现,它会考虑各种处理器,如果能够使用CAS,那么它会尽量使用,如果不能,那么使用操作系统或者其它的库来实现语义上的原子操作。

           这里我们可以使用AtomicLong.compareAndSet。

	private final AtomicLong minTime=new AtomicLong(Long.MAX_VALUE);
        public void updateMinTime(long sample){
	    while (true) {
	        long curMin = minTime.get();
	        if (curMin > sample) {
	            boolean result = minTime.compareAndSet(curMin, sample);
	            if (result) break;
	        } else {
	            break;
	        }
	    }
	}

           我们仔细看一下它的实现。

           它在一个"死循环"里面,首先拿到“当前”的最小值,如果自己比“当前”的最小值大了,那么就不需要更新了,直接break退出。

           如果自己比较小,那么就尝试  boolean result = minTime.compareAndSet(curMin, sample);

           compareAndSet尝试把minTime更新为sample,前提是minTime的值是curMin。可以这样理解这条语句:

    synchronized(this){
        if(minTime==curMin){
             minTime=sample;
             return true;
        }
        return false;
     }

          当然这是语义上的等价,实际上它很可能只是一条机器指令。

           也就是说,我先读取“当前”最小值,然后加锁,然后判断此刻的最小值是否是前面“当前“最小值,如果是,那么说明我还是最小的值(因为我已经加锁,没人能修改)。我可以放心的更新

           如果此刻的最小值不等于前面读过的最小值,那么说明这之间有人修改过了,那么我修改就可能失败(也有可能成功),那么我需要重试。

           使用CAS的优点是:如果竞争不是很激烈,那么性能非常高。因为这是一种“乐观”锁,它不停的尝试获取锁(其实就是CAS),再多线程都不会导致对方等待(当然如果一直不能CAS成功就是在忙等待)。

           当然缺点就是:如果竞争非常激烈,会让很多CPU时间都在CAS上,我们完全可以“阻塞”当前线程,让cpu干点别的。

问题3 按类别统计次数(线程安全的Map)

           比如我们需要按城市来统计查询。那么最容易想到的自然是ConcurrentHashMap<String,Long>。但是同样更新次数时有同步的问题,所以我们可以使用AtomicLong

    private final ConcurrentHashMap<String, AtomicLong> map=new ConcurrentHashMap<String, AtomicLong>();
    public void addCount(String key,long value){
        AtomicLong counter=map.get(key);
        if(counter==null){
            counter=new AtomicLong();
            map.put(key,counter);
        }
        counter.addAndGet(value);
    }

          上面的代码是有问题的,和前面一样,有可能有两个线程同时发现counter为空,同时new了一个AtomicLong,后new的线程会导致前面的值没有累计进去。

           当然我们可以采样类似的方法来double check

    private final ConcurrentHashMap<String, AtomicLong> map=new ConcurrentHashMap<String, AtomicLong>();
    public void addCount(String key,long value){
        AtomicLong counter=map.get(key);
        if(counter==null){
            synchronized(this){
                if(counter==null){ //check again
                    counter=new AtomicLong();
                    map.put(key,counter);
                }
            }
        }
        counter.addAndGet(value);
    }

            同样,我们也可以采样类似CAS的指令------putIfAbsent来实现block free的算法。这种算法在竞争不是非常激烈时很高效,它不会导致线程阻塞而带来切换。

	public void addCounter(String key, long count){
		if(count<=0) return;
		AtomicLong current = map.get(key);
                if(current == null) {
                    AtomicLong al=new AtomicLong();
                    current=map.putIfAbsent(key, al);
        	    if(current == null) current=al;
                }

                assert current!=null;
                current.addAndGet(count);

	}

            putIfAbsent会尝试放入新的key和value,它首先检查这个key是否存在,如果存在,返回老的value,否则插入新的k-v对,并返回老的value。

            如果返回值是null,说明我这个线程更新成功了,那么current就直接使用我构造的al,否则说明别人更新成功,我直接拿到了别人值。

            细心的同学可能有这样的疑问:和Double Check Lock一样,会不会由于编译器或者cpu的reordering导致AtomicLong al=new AtomicLong();没有完全构造好,而其它线程拿到它呢?

            我也有过这样的疑问,同时发现guava的实现和我上面的实现有类似的问题(https://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/util/concurrent/AtomicLongMap.java?name=v11.0-rc1)

            后来询问了一下,是没有问题的,在 ConcurrentMap 的文档里明确写了:Memory consistency effects: As with other concurrent collections, actions in a thread prior to placing an object into a ConcurrentMap as a key or value happen-before actions subsequent to the access or removal of that object from the ConcurrentMap in another thread.

            有兴趣的同学可以看看后面参考文献和邮件通信。

参考资料

1. http://stackoverflow.com/questions/6072040/thread-safe-implementation-of-max

2. http://stackoverflow.com/questions/8477352/global-in-memory-counter-that-is-thread-safe-and-flushes-to-mysql-every-x-increm

3. http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/util/concurrent/AtomicLongMap.html

4. http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ConcurrentMap.html

5. http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ConcurrentHashMap.html#putIfAbsent%28K,%20V%29

6. https://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/util/concurrent/AtomicLongMap.java?name=v11.0-rc1

7. http://www.cs.umd.edu/%7Epugh/java/memoryModel/DoubleCheckedLocking.html

 

[guava] is my counter thread safe and my question about AtomicLongMap?

   Inbox 
Add star 

LiLi

<fancyerii@gmail.com>
Fri, Jun 29, 2012 at 6:46 PM
To: guava-discuss@googlegroups.com
hi all
  I have read http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
and know a little about Java memory model.
  I want to implement a thread safe and efficient Map<String,int>
counter. I found a related question in stackoverflow
http://stackoverflow.com/questions/8477352/global-in-memory-counter-that-is-thread-safe-and-flushes-to-mysql-every-x-increm
  my implementation is :

       private ConcurrentHashMap<String, AtomicLong> counter=new
ConcurrentHashMap<String, AtomicLong>();

       public void addCount(String key, long count){
               if(count<=0) return;
               AtomicLong current = counter.get(key);
               if(current == null) {
                   current=counter.putIfAbsent(
key, new AtomicLong());
                   if(current == null) current=counter.get(key);
               }

               assert current!=null;
               current.addAndGet(count);

       }
  but after I reading
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
. it seems there exists a state that may fail.
  if thread1 call addCount("key",2); and thread2 call
addCount("key",1); at the same time.
  thread1 executes AtomicLong current = counter.get(key);  and gets null.
  then thread 1 execute
         if(current == null) {
                   current=counter.putIfAbsent( k ey, new AtomicLong());
  as compilers/cpus may disorder, new AtomicLong() will not be null
but may be well constructed.
  Then thread 2 call AtomicLong current = counter.get(key); it's not
null but not well constructed. then it call current.addAndGet().
   Will thread 2 crash if current is not well constructed?

   I also find similar implementation in a popular lib -- guava
https://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/util/concurrent/AtomicLongMap.java?name=v11.0-rc1
   it may also fail like my codes.
   e.g. thread 1 call atomic = map.putIfAbsent(key, new AtomicLong(delta));
          thread 2 get a not null atomic and call it's get();

public long addAndGet(K key, long delta) {
   outer: for (;;) {
     AtomicLong atomic = map.get(key);
     if (atomic == null) {
       atomic = map.putIfAbsent(key, new AtomicLong(delta));
       if (atomic == null) {
         return delta;
       }
       // atomic is now non-null; fall through
     }

     for (;;) {
       long oldValue = atomic.get();
       if (oldValue == 0L) {
         // don't compareAndSet a zero
         if (map.replace(key, atomic, new AtomicLong(delta))) {
           return delta;
         }
         // atomic replaced
         continue outer;
       }

       long newValue = oldValue + delta;
       if (atomic.compareAndSet( oldValue , newValue)) {
         return newValue;
       }
       // value changed
     }
   }
 }

--
guava-discuss@googlegroups.com
Project site: http://guava-libraries.googlecode.com
This group: http://groups.google.com/group/guava-discuss
 
This list is for general discussion.
To report an issue: http://code.google.com/p/guava-libraries/issues/entry
To get help: http://stackoverflow.com/questions/ask (use the tag "guava")
Reply | Reply to all | Forward | Print | Delete | Show original

Add star 

tsuna

<tsunanet@gmail.com>
Sat, Jun 30, 2012 at 12:01 AM
To: LiLi <fancyerii@gmail.com>
Cc: guava-discuss@googlegroups.com
On Fri, Jun 29, 2012 at 3:46 AM, LiLi <fancyerii@gmail.com> wrote:
>    Will thread 2 crash if current is not well constructed?

No.  This issue won't arise with ConcurrentHashMap because:
  - Either the key is already in the map, in which case putIfAbsent
returns the existing entry;
  - Or the key is not already in the map, in which case putIfAbsent
_atomically_ inserts it in the map and returns null.

In the latter case, as documented by the contract of ConcurrentMap
( http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ConcurrentMap.html)
"placing an object into a ConcurrentMap as a key or value
happen-before actions subsequent to the access".

In other words, because the code is guaranteed to be implemented in
such a way that there is a happen-before relationship between the call
to putIfAbsent in Thread 1, and the call to get in Thread 2.  So
Thread 2 will either see a correctly constructed AtomicLong, or it
will see null and will then race with thread 1 to insert the key in
putIfAbsent, which is perfectly fine as only one thread will
ultimately manage to create the key.

Put differently, it's as if the map was guaranteed to be implemented
with one lock per key, and that the lock was used to ensure that all
access to each entry in the map is done in a thread-safe manner.  Of
course in practice the implementation can be different, for example
ConcurrentHashMap is traditionally implemented by having multiple
internal hash maps, each of which is guarded by a single lock, and
that lock is only acquired on writes, whereas reads rely only on
volatile.  Guava's LoadingCache is implemented like that too (in fact
its code is actually a fork of that of ConcurrentHashMap).  There are
obviously many other implementations possible, most notable the one
used by NonBlockingHashMap, which is completely lock-free.

 

is my counter thread safe?

   Inbox 
Add star 

Li Li

<fancyerii@gmail.com>
Fri, Jun 29, 2012 at 6:41 PM
To: javamemorymodel-discussion@cs.umd.edu
hi all
   I have read http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
and know a little about Java memory model.
   I want to implement a thread safe and efficient Map<String,int>
counter. I found a related question in stackoverflow
http://stackoverflow.com/questions/8477352/global-in-memory-counter-that-is-thread-safe-and-flushes-to-mysql-every-x-increm
   my implementation is :

        private ConcurrentHashMap<String, AtomicLong> counter=new
ConcurrentHashMap<String, AtomicLong>();

        public void addCount(String key, long count){
                if(count<=0) return;
                AtomicLong current = counter.get(key);
                if(current == null) {
                    current=counter.putIfAbsent( key, new AtomicLong());
                    if(current == null) current=counter.get(key);
                }

                assert current!=null;
                current.addAndGet(count);

        }
   but after I reading
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
. it seems there exists a state that may fail.
   if thread1 call addCount("key",2); and thread2 call
addCount("key",1); at the same time.
   thread1 executes AtomicLong current = counter.get(key);  and gets null.
   then thread 1 execute
          if(current == null) {
                    current=counter.putIfAbsent( key, new AtomicLong());
   as compilers/cpus may disorder, new AtomicLong() will not be null
but may be well constructed.
   Then thread 2 call AtomicLong current = counter.get(key); it's not
null but not well constructed. then it call current.addAndGet().
    Will thread 2 crash if current is not well constructed?

    I also find similar implementation in a popular lib -- guava
https://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/util/concurrent/AtomicLongMap.java?name=v11.0-rc1
    it may also fail like my codes.
    e.g. thread 1 call atomic = map.putIfAbsent(key, new AtomicLong(delta));
           thread 2 get a not null atomic and call it's get();

public long addAndGet(K key, long delta) {
    outer: for (;;) {
      AtomicLong atomic = map.get(key);
      if (atomic == null) {
        atomic = map.putIfAbsent(key, new AtomicLong(delta));
        if (atomic == null) {
          return delta;
        }
        // atomic is now non-null; fall through
      }

      for (;;) {
        long oldValue = atomic.get();
        if (oldValue == 0L) {
          // don't compareAndSet a zero
          if (map.replace(key, atomic, new AtomicLong(delta))) {
            return delta;
          }
          // atomic replaced
          continue outer;
        }

        long newValue = oldValue + delta;
        if (atomic.compareAndSet( oldValue, newValue)) {
          return newValue;
        }
        // value changed
      }
    }
  }
Reply | Reply to all | Forward | Print | Delete | Show original

Add star 

Pavel Rappo

<pavel.rappo@gmail.com>
Fri, Jun 29, 2012 at 6:54 PM
To: Li Li <fancyerii@gmail.com>
Cc: concurrency-interest <concurrency-interest@cs.oswego.edu>
Hi,

This 'counter.get(key)' cannot simply return "not well constructed"
object. The reason is that you use 'putIfAbsent' to put it in the map.
Either 'get' will return 'null' or it will return perfectly valid
AtomicLong object. There's a happen-before edge between these two
actions.
> ______________________________ _________________
> Javamemorymodel-discussion mailing list
> Javamemorymodel-discussion@cs.umd.edu
> https://mailman.cs.umd.edu/mailman/listinfo/javamemorymodel-discussion
>


--
Sincerely yours, Pavel Rappo.
Reply | Reply to all | Forward | Print | Delete | Show original

Add star 

Li Li

<fancyerii@gmail.com>
Fri, Jun 29, 2012 at 7:11 PM
To: Pavel Rappo <pavel.rappo@gmail.com>
do you mean my counter is safe?
putIfAbsent has a happen-before semantic? any document about this? Or
only current implementation guarantee this?

Add star 

Pavel Rappo

<pavel.rappo@gmail.com>
Fri, Jun 29, 2012 at 7:24 PM
To: Li Li <fancyerii@gmail.com>
Cc: concurrency-interest <concurrency-interest@cs.oswego.edu>
1. I can't see any obvious flaws in it.
2. http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html
(see "Memory Consistency Properties")

Add star 

Li Li

<fancyerii@gmail.com>
Fri, Jun 29, 2012 at 7:30 PM
To: Pavel Rappo <pavel.rappo@gmail.com>

thanks.

在 2012-6-29 晚上7:24,"Pavel Rappo" < pavel.rappo@gmail.com>写道:

Add star 

Jeremy Manson

<jeremy.manson@gmail.com>
Mon, Jul 2, 2012 at 2:29 AM
To: Pavel Rappo <pavel.rappo@gmail.com>
Cc: Li Li <fancyerii@gmail.com>, concurrency-interest <concurrency-interest@cs.oswego.edu>
The counter field should be final, because another thread could read
the enclosing object before it is finished being constructed (unless
youve taken other precautions to prevent that.

Jeremy
> ______________________________ _________________
> Concurrency-interest mailing list
> Concurrency-interest@cs.oswego.edu
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest

 

java使用线程安全的单例模式实现网站计数器 四个java类 附上代码(比较简单)用于设计模式课程需要 登录类 package mytest.demo.WebCount; public class Login implements Runnable { // 登录名称 private String loginName; public String getLoginName() { return loginName; } public void setLoginName(String loginN 阅读详情

相关推荐

Flink累加器的应用:大数据处理

Flink累加器的应用:大数据处理Flink是一种流式处理框架,广泛应用于大数据处理和实时分析。在Flink中,累加器是一种重要的工具,用于在分布式环境中进行计数和聚合操作。本文将详细介绍Flink累加器的使用,并提供相应的源代码示例。一、什么是累加器? 在Flink中,累加器是一种特殊的变量,用于收集和聚合分布式任务的统计信息。累加器可以在不同任务之间共享和更新,从而实现全局的计数和聚合。它们通常用于收集诊断信息、计数特定事件的发生次数或跟踪任务的进度。二、累加器的类型 Flink提供了多种类型的累加器,

学习使你进步。 249

线程安全的计数器

不能通过编译的例子。 Creature creature = new Creature();是一个局部变量声明语句(local variable declaration statement) JLS 4.4,java语言规范不允许一个局部变量声明语句作为一条语句在循环中重复执行。一个本地变量声明作为一条只能直接出现在一个语句块中。(一个语句块又括号以及其中包括的语句和声明构成)  

业精于勤荒于嬉;行成于思,毁于随。 610

Java线程安全的计数器简单实现代码示例

主要介绍了Java线程安全的计数器简单实现代码示例,具有一定参考价值,需要的朋友可以了解下。

Android基础学习(二十)—— 线程安全

线程安全

123 1231

Halcon工业相机开发全流程:从SDK配置到实时图像采集(C++版)

本文详细介绍了使用C++进行Halcon工业相机开发的完整流程,从SDK配置到实时图像采集的实现。涵盖环境搭建、设备初始化、回调机制、参数控制等关键环节,并提供异常处理和实战案例,帮助开发者快速掌握工业视觉系统的开发技巧。

weixin_33728268的博客 512

告别复杂配置!5分钟在Vue/React项目中集成讯飞语音听写WebSocket API

本文详细介绍了如何在Vue/React项目中快速集成讯飞语音听写WebSocket API,实现高效语音识别功能。通过对比不同通信方案,突出WebSocket在实时语音交互中的优势,并提供框架适配、音频处理及性能优化等关键技术点,帮助开发者5分钟内完成专业级语音听写功能集成。

weixin_28730353的博客 274

高并发下的计数器实现方式:AtomicLong、LongAdder、LongAccumulator

计数器是并发编程中非常常见的一个需求,例如统计网站的访问量、计算某个操作的执行次数等等。在高并发场景下,如何实现一个线程安全的计数器是一个比较有挑战性的问题。本文将介绍几种常用的计数器实现方式,包括AtomicLong、LongAdder和LongAccumulator,并深入讲解其中的CAS操作。

weixin_40381772的博客 4283

如何点亮一颗sensor

要点亮一个sensor需要从,时钟、sensorI2C,供电,reset脚,mipi的配置几个方面一一确认。 reset脚拉高 给sensor的时钟有没有。(目前用到的常为24MHZ) 供电 查看sensor的数据手册,看有几个电源,用示波器量一下是否为手册规定的电压 比如:DVDD 数字电压: AVDD模拟电压 DOVDD芯片引脚出来的电压等为多少。 sensor的I2C能否抓到 将I2C的口对应上,用示波器量取i2c的波形。 检查有应答并且数据正确。读写地址是否正...

weixin_40787463的博客 3646

Java笔试面试题AI答之线程(13)

在停止线程时,推荐使用退出标志或interrupt方法,因为它们提供了更安全和可控的方式来结束线程的执行。而stop方法由于存在安全风险,应该避免使用。在实际编程中,应根据具体需求和场景选择最适合的线程停止方法。锁顺序死锁是并发编程中常见的一种死锁类型,它发生在多个线程以不同的顺序尝试获取相同的锁集合时。锁顺序死锁是指当多个线程在访问多个资源时,每个线程都按照各自的顺序获取锁,而这些顺序在某些情况下相互冲突,导致线程相互等待对方释放锁,从而形成死锁。

学无止境,止于至善 1340

ESP32-S3开发环境避坑指南:VS Code+ESP-IDF离线安装全流程(附国内镜像加速)

本文提供了一份详细的ESP32-S3开发环境离线安装指南,重点解决网络依赖问题。通过整合国内镜像加速,手把手教你完成VS Code、ESP-IDF框架、工具链及Python依赖的本地化部署,实现稳定、可重复的开发环境搭建,大幅提升IoT开发效率。

peach的博客 961

ILI9488 TFT屏驱动与触摸协同开发指南

TFT-LCD显示模块是嵌入式人机交互系统的核心外设,其工作原理依赖于专用显示控制器(如ILI9488)对GRAM显存的精确读写,并通过SPI等串行总线接收指令与图像数据。理解RGB接口时序、寄存器初始化流程及DC/CS信号控制逻辑,是实现稳定显示的基础技术前提;而四线电阻式触摸功能则需配合XPT2046类ADC控制器,通过独立SPI或模拟时序完成坐标采集。该方案兼顾低功耗、高集成与工程可移植性,广泛应用于工业HMI、仪器仪表及STM32F407等MCU平台的教学与产品开发。本文围绕3.5英寸320×480

weixin_33814090的博客 148

线程安全-计数器

1、计数器实现存在线程安全问题,主要由于value++非原子性操作导致 import java.util.Random; public class Sequence { private int value; public int getNext(){ return value++; } //测试 public static void main(String[] ar...

yanghw117的博客 1143

Open3D 进阶(32)渐进三角网(PTD)地面滤波

渐进三角网地面滤波算法(Progressive TIN Densification, PTD)是一种广泛应用于机载LiDAR点云数据处理的滤波方法,旨在从复杂场景中精确分离地面点,以生成数字高程模型(DEM)。

点云侠的博客 1533

性能监控新范式:Folly统计指标工具包极速上手

你是否还在为应用性能监控发愁?服务器响应延迟波动大却找不到瓶颈?用户投诉卡顿但日志里只有枯燥的数字?本文将带你掌握Facebook开源C++库Folly中最实用的性能统计工具,无需复杂配置即可实现专业级监控,让你5分钟内搭建起精准的性能观测体系。 读完本文你将获得: - 3个核心统计组件的零代码集成方案 - 99%分位延迟计算的最优实践 - 线程安全的计数器实现模板 - 生产环境验证的性能监控架...

gitblog_00358的博客 364

ubuntu20.04屏幕亮度无法调节(亮度条调节无效)的简单靠谱解决方案及踩坑历程

0 前言   网上关于ubuntu系统亮度无法调节的问题的解决方法有很多,但是绝大部分并没有什么用(这也是我自己的亲身体会),或者并不靠谱。在遍尝各路方法过后,我终于解决了这一棘手的问题(毕竟最大亮度真的非常刺眼,无法长时间直视),在此记录一下踩坑历程以及最后的解决方案。 1 Naive方法   由于使用需要,最近在电脑原有的win10基础上安装了ubuntu双系统。我安装的是当前新版的ubuntu20.04.2.0,靠谱的安装教程可以参考这里。   之前师兄说双ssd安装双系统会有系统启动引导的问题出现,

zeeq的博客 4万+

43_AI短片实战第十六弹:AIGC补充特写镜头——首尾帧衔接与多图抽卡技巧

本文介绍了使用Templa AI平台生成"怪兽爬车"特写镜头并衔接"甩尾摔下"动作的完整流程。首先通过AI生成4张怪兽趴在车顶的特写画面,重点选择展示怪兽与车顶关系的构图(16:9比例)。然后将选定的首帧图与已有尾帧图输入AI,生成7秒左右的过渡视频,实现从"稳定趴车"到"开始摔落"的自然衔接。整个过程展示了单图抽卡策略、首尾帧选择标准,以及如何利用AI生成时间进行其他剪辑任务的并行处理方法。最终产出包括特写首帧图、过渡视频文件和更新后的剪辑项目。

Open 75

八大排序算法比较与交换次数统计工具

排序算法的性能本质不在于运行时间,而在于其核心原子操作——比较与交换。理解冒泡、快排、归并等算法在不同数据分布下的实际行为,需剥离CPU负载、缓存、语言开销等干扰,直击算法逻辑内核。比较次数反映决策频次,交换次数体现数据搬移成本,二者共同构成算法的‘行为指纹’。该统计方法广泛应用于算法教学验证、教材案例复现、性能瓶颈定位及增量策略评估(如希尔排序序列选型)。通过细粒度捕获每一次比较和交换,可真实揭示插入排序的自适应性、选择排序的固定比较特性,以及计数排序零比较的本质差异。

weixin_32430941的博客 261

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Spark-累加器Accumulator图文详解

Spark累加器

喻师傅的学习笔记 848

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

3000+ 张成品设计随用随取:戴森球计划工厂蓝图库 FactoryBluePrints

FactoryBluePrints 是面向《戴森球计划》的社区工厂蓝图仓库,汇集数千张经过社区实测的戴森球计划蓝图,覆盖开荒、产线、能源、物流到戴森球建造的完整流程。无论你是刚起步、还在手动画传送带的新手,还是想继续做工厂布局优化的老玩家,都可以直接导入现成的工厂设计模板,把时间花在产能规划上而不是重复摆建筑。 ## 适合哪些玩家使用 - **开荒期新手**:不想自己研究产线比例,可以直接取用

gitblog_00119的博客 206

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

4 多线程统计次数问题:即count++

多线程统计次数问题:即count++ 场景:日志需要统计每天数据上传的次数和上传的数据量。      如果是单线程可以使用简单的int count = 0;count++,但很多情况都是多线程环境所以就不能单纯的使用count++了!!!      多线程环境采用 java.util.concurrent.atomic 下的AtomicLong类,该类的介绍可看API文档。可以用原子方式更新...

xiaoxiaoniaoQ的博客 3476

设计线程安全的高性能计数器

方案适用场景优点缺点AtomicLong低并发,简单计数无锁,CAS 轻量级高并发时 CAS 自旋LongAdder高并发写,读少(推荐)分段累加,减少竞争sum()稍慢复杂逻辑,低性能要求简单易用锁竞争高时性能差极高吞吐,允许短暂不一致无竞争,性能极高读取可能不精确分片计数(如按 key 统计)扩展性强,支持多 key内存占用较高。

2203_75376529的博客 1613

最全面的jemalloc计数器实现:从源码到内存统计实战

你是否曾为内存泄漏问题困扰数周?是否想知道生产环境中内存碎片如何悄然侵蚀系统性能?jemalloc的counter计数器组件正是解决这些问题的关键。本文将带你深入jemalloc源码,剖析counter模块的实现原理,掌握内存使用统计的核心方法,让你轻松应对复杂的内存管理挑战。读完本文,你将能够:理解计数器的线程安全设计、掌握内存使用统计的实现方式、学会利用jemalloc的统计功能优化应用性能。...

gitblog_00404的博客 501

什么是线程安全?

在编程里,特别是当程序能同时做很多事情(多线程)时,线程安全就显得尤为重要了。

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 5400

基于CAS线程安全实现计数器

 在多线程环境下,实现一个CAS原理的线程安全的技术器,并与不使用CAS算法的计数器进行比较。 package com.feiyu.text; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * @Created by 猎风工作...

青衣煮茶 1万+

java 线程安全 计数_Java线程安全的计数器简单实现代码示例

前几天工作中一段业务代码需要一个变量每天从1开始递增。为此自己简单的封装了一个线程安全的计数器,可以让一个变量每天从1开始递增。当然了,如果项目在运行中发生重启,即便日期还是当天,还是会从1开始重新计数。所以把计数器的值存储在数据库中会更靠谱,不过这不影响这段代码的价值,现在贴出来,供有需要的人参考。package com.hikvision.cms.rvs.common.util;import ...

weixin_39708854的博客 335

java 线程安全 计数_java – 实现线程安全共享计数器的功能方法

我对Scala和函数式编程比较陌生,我喜欢使用不可变对象的想法,我可以避免许多线程安全陷阱.有一件事仍然困扰着我,这是用于教授线程安全的经典示例 – 共享计数器.我想知道是否可以实现线程安全计数器(在此示例中为请求计数器),使用不可变对象和功能概念,并完全避免同步.所以在这里参考首先是计数器的经典可变版本(请原谅我的公共成员变量,仅为了简洁示例)可变,非线程安全版本:public class Se...

weixin_31232695的博客 372
上一篇: build qt webkit with vs2008 in windows
下一篇: ubuntu下mysql workbench 没响应的解决办法
fancyerII
博客等级 码龄23年 48粉丝 164原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值