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

利用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

人工智能训练师技能等级认定四级技能考核试卷-样题4

本文为人工智能训练师四级技能考核试卷,包含六个实操考核项目,总时长150分钟。考核内容涵盖:1)车牌数据集规范评估与格式处理;2)车牌数据集采集代码修改与数据处理;3)房价预测数据集加载、预处理与可视化分析;4)车牌标注数据集审核与修正;5)螺丝螺母检测数据集的平台创建与共享;6)螺丝螺母数据的去噪处理与标注优化。要求考生使用Python编程、数据处理平台和标注工具完成各项任务,重点考察数据加载、格式转换、缺失值处理、数据可视化、标注审核及平台操作等技能。所有操作需在配备Python3.7和深度学习平台的W

weixin_42221934的博客 348

VL53L0X测距不准?手把手教你做RefSPAD校准与环境补偿(附Arduino/STM32代码)

本文详细解析了VL53L0X激光测距传感器精度不足的原因,并提供了RefSPAD校准与环境补偿的完整解决方案。通过Arduino和STM32代码示例,手把手教你实现毫米级测距稳定性,适用于机器人避障、工业检测等场景。

weixin_28744423的博客 256

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

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

weixin_40381772的博客 4283

保姆级教程:用STM32H743+TJA1042T玩转FDCAN,CubeMX配置1M/5M双速率通信(附完整代码)

本文提供STM32H743与TJA1042T实现FDCAN双速率通信的保姆级教程,详细讲解CubeMX配置1M/5M双速率通信的全过程,包括硬件连接、时钟树配置、过滤器设置及中断管理,并附完整代码示例,助力开发者快速掌握FDCAN高速通信技术。

weixin_30698297的博客 473

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

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

学无止境,止于至善 1340

智能车竞赛独轮组信标灯系统全解析:从硬件选型到实战调试

本文全面解析了智能车竞赛独轮组信标灯系统的核心原理与实战应用。从硬件架构(灯板、灯罩、控制器)的深度剖析,到红外信号(特别是40kHz调制信号)的接收与解码策略,再到车模的灭灯机构设计与路径规划,提供了从硬件选型到软件调试的全链路指南。文章重点分享了实战调试技巧与常见问题排查方法,旨在帮助参赛队伍深入理解系统,提升车模的识别精度与比赛表现。

e4f5g6h的博客 1124

告别偏移地址!用C# S7PLUS驱动读写西门子S7-1500符号地址(TIA Portal V17+环境配置)

本文详细介绍了如何在TIA Portal V17+环境中使用C# S7PLUS驱动读写西门子S7-1500 PLC的符号地址,告别传统的偏移地址方式。通过配置OpenSSL、集成S7PLUS驱动库,以及实战案例展示符号地址的读写方法,显著提升代码可读性和维护效率。文章还涵盖了环境准备、高级配置及常见问题解决方案,助力工业自动化开发者实现更高效的PLC通讯。

weixin_28720767的博客 315

线程安全-计数器

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

博途PLC PID仿真LSim_PT1库算法(一阶系统差分方程推导全过程)

博途PLC的PID仿真可以参看下面这篇博客,这篇文章我们主要论坛仿真库LSim的算法由来。如何利用博途PLC PID_Compact 进行PID仿真(一阶系统)_RXXW_Dor的博客-优快云博客博途PLC PID控制器 PID_Compact FB如何使用可以参看下面这篇文章,这里不再赘述。

RXXW_Dor的博客 2858

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

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

gitblog_00358的博客 364

飞腾UEFI配置GPIO—飞腾FT2000/4 (D2000/8) GPIO引脚配置及使用

讲解了在飞腾UEFI代码中如何添加GPIO配置相关代码,并以GPIO1_B0—B7为例,将这8个引脚配置为GPIO工作模式并默认输出低电平

qq_30135687的博客 5343

从苏宁电器到卡巴斯基第02篇:我的本科时光(中)

怎么还分班了呢? 大概是在大二第二学期的时候吧,学院突然通知我们要分班。因为我们目前所有学生都是属于工商管理专业的,而从下个学期开始,就要分为会计、市场营销以及工商管理这三个方向。这还真是打了我一个措手不及,我都不知道还有分班这一回事。不过也没关系,我继续选择我的工商管理就好了。不过,在实际的选择结果上,像会计和工商管理这种学科,选的人自然是占了绝大多数,而选择市场营销的,大概也就是十

Gleam的专栏 9605

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

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

weixin_32430941的博客 261

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Spark-累加器Accumulator图文详解

Spark累加器

喻师傅的学习笔记 848

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

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

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

gitblog_00119的博客 206

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

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、付费专栏及课程。

余额充值