当前位置: 首页 > 科技观察

Java中有哪些无锁技术可以解决并发问题?如何使用?

时间:2023-03-18 19:41:02 科技观察

除了使用synchronized和Lock来加锁之外,Java中还有很多工具类可以在不加锁的情况下解决并发问题。它是一个原子类,基于sun.misc.Unsafe实现。为了解决并发问题,CPU提供了CAS指令,全称CompareAndSwap,即比较交互CAS指令需要3个参数,变量,比较值,新值。当变量的当前值等于比较值时,变量被更新为新值。CAS是保证CPU硬件级别原子性的CPU指令。java.util.concurrent.atomic包中的原子分为:原子基础数据类型、原子对象引用类型、原子数组、原子对象属性更新器、原子累加器原子基础数据类型:AtomicBoolean、AtomicInteger、AtomicLong原子对象引用类型:AtomicReference、AtomicStampedReference、AtomicMarkableReference原子数组:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray原子对象属性更新:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater原子累加器:DoubleAccumulator、DoubleAdder、LongAccumulatorAtomicIntegerpackageconstxiong.port.java0aconcur6util.concurrent.atomic.AtomicInteger;/***测试原子类AtomicInteger**@authorConstXio??ng*/publicclassTestAtomicInteger{//计数变量staticvolatileAtomicIntegercount=newAtomicInteger(0);publicstaticvoidmain(String[]args)throwsInterruptedException{//线程1计数添加10000Threadt1=newThread(()->{for(intj=0;j<10000;j++){count.incrementAndGet();}System.out.println("threadt1countplus10000end");});//线程2Add10000Threadt2=要计数newThread(()->{for(intj=0;j<10000;j++){count.incrementAndGet();}System.out.println("threadt2count加10000结束");});//启动线程1t1。start();//启动线程2t2.start();//等待线程1执行t1.join();//等待线程2执行t2.join();//打印计数变量System.out.println(count.get());}}按预期打印结果threadt2countplus10000endthreadt1countplus10000end200002.线程本地存储java.lang.ThreadLocal类用于线程本地化存储线程本地化存储是针对每个线程创建一个只有该线程才能查看和修改值的变量。一个典型的使用例子是spring在处理数据库事务时,使用ThreadLocal为每个线程存储自己的数据库连接Connection。注意在使用ThreadLocal时,当变量没有被使用时,一定要调用remove()方法移除变量,否则可能会造成内存泄漏。Examplepackageconstxiong.concurrency.a026;/***测试原子类AtomicInteger**@authorConstXio??ng*/publicclassTestThreadLocal{//线程本地存储变量privatestaticfinalThreadLocalTHREAD_LOCAL_NUM=newThreadLocal(){@OverrideprotectedIntegerinitialValue(){//初始化valuereturn0;}};publicstaticvoidmain(String[]args){for(inti=0;i<3;i++){//启动三个线程Threadt=newThread(){@Overridepublicvoidrun(){add10ByThreadLocal();}};t.start();}}/***线程本地存储变量加5*/privatestaticvoidadd10ByThreadLocal(){try{for(inti=0;i<5;i++){Integern=THREAD_LOCAL_NUM.get();n+=1;THREAD_LOCAL_NUM.set(n);System.out.println(Thread.currentThread().getName()+":ThreadLocalnum="+n);}}finally{THREAD_LOCAL_NUM.remove();//移除变量}}}每个线程的最后一个值被打印到5Thread-0:ThreadLocalnum=1Thread-2:ThreadLocalnum=1Thread-1:ThreadLocalnum=1Thread-2:ThreadLocalnum=2Thread-0:ThreadLocalnum=2Thread-2:ThreadLocalnum=3Thread-0:ThreadLocalnum=3Thread-1:ThreadLocalnum=2Thread-0:ThreadLocalnum=4Thread-2:ThreadLocalnum=4Thread-0:ThreadLocalnum=5Thread-1:ThreadLocalnum=3Thread-2:ThreadLocalnum=5Thread-1:ThreadLocalnum=4Thread-1:ThreadLocalnum=5三、copy-on-write根据英文名可以看出copy-on-write是必需的,这反映了一个Java中的写时复制容器包括:CopyOnWriteArrayList、CopyOnWriteArraySet。涉及到数组的全量拷贝,所以比较耗内存,比较适合在写入较少的时候使用。CopyOnWriteArrayList的一个简单例子,这里只是为了说明如何使用CopyOnWriteArrayList,它是线程安全的。这种场景不适合使用CopyOnWriteArrayList,因为写多读少。packageconstxiong.concurrency.a026;importjava.util.ArrayList;importjava.util.List;importjava.util.Random;importjava.util.concurrent.CopyOnWriteArrayList;/***测试写时复制*@authorConstXio??ng*/publicclassTestCopyOnWrite{privatestaticfinalRandomR=newRandom();privatestaticCopyOnWriteArrayListcowList=newCopyOnWriteArrayList();//privatestaticArrayListcowList=newArrayList();publicstaticvoidmain(String[]args)throwsInterruptedException{ListthreadList=newArrayList();//启动1000个线程,向cowList添加5个随机整数for(inti=0;i<1000;i++){Threadt=newThread(()->{for(intj=0;j<5;j++){//休眠10毫秒,让线程同时向cowList中添加整数,造成并发问题try{Thread.sleep(10);}catch(InterruptedExceptione){e.printStackTrace();}cowList.add(R.nextInt(100));}});t.start();threadList.add(t);}for(Threadt:threadList){t.join();}System.out.println(cowList.size());}}打印结果5000如果privatestaticCopyOnWriteArrayListcowList=newCopyOnWriteArrayList();改为privatestaticArrayListcowList=newArrayList();打印结果为小于5000的整数

最新推荐
猜你喜欢