线程安全性(1)

保证线程安全性的三点:

一、原子性:提供互斥访问,在同一时刻只能有一个线程来对它进行操作。

二、可见性:一个线程对主内存的修改可以让其它线程及时的观察到。

三、有序性:由于cpu和jvm对指令的重排序,会使得其他线程观察到的该线程操作是杂乱无序的。

 


java中提供了Atomic包的AtomicXXX类、Lock类以及synchronized关键字来实现原子性

 

1.Atomic类的使用

 1 public class AtomicExample1 {
 2     public static int clientTotal=5000;  //请求总数
 3     
 4     public static int threadTotal=200;   //同时请求的线程数  
 5     
 6     public static AtomicInteger count=new AtomicInteger(0);  //使用AtomicInteger类来进行并发的计数
 7     
 8     public static void main(String[] args)throws Exception {
 9         ExecutorService executorService=Executors.newCachedThreadPool();
10         final Semaphore semaphore=new Semaphore(threadTotal);
11         final CountDownLatch countDownLatch=new CountDownLatch(clientTotal);
12         for(int i=0;i<clientTotal;i++) {
13             executorService.execute(()->{
14                 try {
15                     semaphore.acquire();
16                     add();
17                     semaphore.release();
18                 } catch (Exception e) {
19                     log.error("Exception", e);    
20                 }
21                 countDownLatch.countDown();
22             });
23         }
24         countDownLatch.await();
25         executorService.shutdown();
26         log.info("count:{}",count.get());
27     }
28     
29     private static void add() {
30         count.incrementAndGet();  //增加计数值+1的方法
31     }
32 }

 

结果等于总请求数5000

 

原理:其方法中使用的CAS操作  (compareAndSwap)

 1  public final int getAndAddInt(Object o, long offset, int delta) {
 2         int v;
 3         do {
 4             //*保证缓存一致性同时从给定偏移量处的对象o
 5             v = getIntVolatile(o, offset);
 6         } while (!compareAndSwapInt(o, offset, v, v + delta));
 7         //*不断地重新获取内存中的值,直至成功CAS
 8         return v;
 9     }
10 
11 public final int getAndAdd(int delta) {
12         return unsafe.getAndAddInt(this, valueOffset, delta);
13     }

CAS操作将操作对象的地址(o)和它的值(offset)进行本地方法的运算,如果当前对象值在判断时依然与之前的到的本地结果(v)一致则说明没有其他线程操作过,对值进行加操作,若不一致则不断进行循环直到符合本地结果。

CAS可以有效避免脏读,但是在竞争激烈的情况可能会出现循环时间过长导致性能下降。

 

ABA问题:CAS操作时出现的,某一线程将值由A修改为B之后又修改到A值,虽然可以通过CAS操作但是它的值是有过变化的,与我们的设计思想不符合。

解决方法版本号机制:若线程修改过该对象的值,则该对象的版本号进行递增。

Atomic包中的AtomicStampReference类使用了该机制

 

2.Lock类依赖于特殊的CPU指令,其实现类有ReentrantLock。

3.synchronized关键字依赖于JVM

 

三者之间的比较

1、synchronized:不可中断的锁,可读性好,适合竞争不激烈的场景

2、Lock类,可中断锁,在竞争激烈的情况下依然保持良好

3、Atomic包,无锁算法,在激烈时性能依然良好,比Lock类更优,只能同步一个值

 

posted @ 2020-02-12 22:01  花园里的大猪  阅读(129)  评论(0)    收藏  举报