可重入锁作为并发共享数据,保证一致性的工具,JAVA平台上有很多实现(如synchronized(重量级)和ReentrantLock(轻量级等)。这些已经编写并提供的锁为我们的开发提供了方便。可重入锁,也叫递归锁,指的是同一个线程。外层函数获取到锁后,内层函数递归函数还有获取锁的代码,但不受影响。JAVA环境下,ReentrantLock和synchronized都是可重入锁;}publicsynchronizedvoidset(){System.out.println("name:"+Thread.currentThread().getName()+"set();");}@Overridepublicvoidrun(){get();}publicstaticvoidmain(String[]args){Testss=newTest();newThread(ss).start();newThread(ss).start();newThread(ss).start();newThread(ss).start();}}publicclassTest02extendsThread{ReentrantLocklock=newReentrantLock();publicvoidget(){lock.lock();System.out.println(Thread.currentThread().getId());set();lock.unlock();}publicvoidset(){lock.lock();System.out.println(Thread.currentThread().getId());lock.unlock();}@Overridepublicvoidrun(){get();}publicstaticvoidmain(String[]args){Testss=newTest();newThread(ss).start();newThread(ss).start();newThread(ss).start();}}读写锁类比Java中的锁(LocksinJava)。读写锁稍微复杂一点。假设你的程序涉及到对一些共享资源的读写操作,而且写操作没有读操作那么频繁。当没有写操作时,两个线程同时读取一个资源是没有问题的,所以应该允许多个线程同时读取共享资源。但是,如果一个线程想要写入这些共享资源,则其他线程不应读取或写入该资源。不能共存)。这就需要读/写锁来解决这个问题。Java5已经在java.util.concurrent包中包含了读写锁。不过,我们应该了解其实施背后的基本原理。publicclassCache{staticMapmap=newHashMap();staticReentrantReadWriteLockrwl=newReentrantReadWriteLock();staticLockr=rwl.readLock();staticLockw=rwl.writeLock();//获取某个key对应的值publicstaticfinalObjectget(Stringkey){r.lock();try{System.out.println("读取操作,key:"+key+"start");Thread.sleep(100);Objectobject=map.get(key);System.out.println("读取操作,key:"+key+"end");System.out.println();returnobject;}catch(InterruptedExceptione){}finally{r.unlock();}returnkey;}//设置key对应的值,返回旧值publicstaticfinalObjectput(Stringkey,Objectvalue){w.lock();try{System.out.println("写操作,key:"+key+",value:"+value+"Start.");Thread.sleep(100);Objectobject=map.put(key,value);System.out.println("写操作,key:"+key+",value:"+value+"end.");System.out.println();returnobject;}catch(InterruptedExceptione){}finally{w.unlock();}returnvalue;}//清除所有内容{for(inti=0;i<10;i++){Cache.put(i+"",i+"");}}}).start();newThread(newRunnable(){@Overridepublicvoidrun(){for(inti=0;i<10;i++){Cache.get(i+"");}}}).start();}}悲观锁,乐观锁乐观锁一直认为不会有并发问题,去取每次到数据的时候,总是认为没有其他线程会修改数据,所以不会加锁,但是更新的时候会判断之前有没有其他线程修改过数据,一般使用版本号机制或CAS操作实现版本的方法:一般在数据表中增加一个数据版本号version字段,表示数据被修改的次数。当数据被修改时,版本值会加一。当线程A要更新数据值时,它在读取数据的同时也会读取版本值。提交更新时,如果刚刚读取的版本值等于当前数据库中的版本值,则更新,否则重试更新操作,直到更新成功。核心SQL语句updatetablesetx=x+1,version=version+1whereid=#{id}andversion=#{version};CAS操作方式:比较交换或比较设置,涉及三个操作数,数据所在内存值,期望值,新值。当需要更新时,判断当前内存值是否等于之前获取的值。如果相等,则用新值更新。如果失败,则重试。一般是自旋操作,即不断重试。悲观锁总是假设最坏的情况。每次取数据时,都认为其他线程会修改,所以会加锁(读锁、写锁、行锁等)。当其他线程要访问数据时,需要阻塞挂掉。可以依赖数据库来实现,比如行锁,读锁,写锁,都是操作前加锁。在Java中,synchronized的思想也是一种悲观锁。原子类java.util.concurrent.atomic包:一个用于原子类的小型工具包,支持在单个变量上解锁的线程安全编程。原子变量类相当于一个广义的volatile变量,可以支持原子的和有条件的Read-modify-write操作。AtomicInteger表示一个int类型的值,并提供get和set方法。这些Volatile类型的int变量在读写上具有相同的内存语义。它还提供原子的compareAndSet方法(如果成功,将实现与读/写volatile变量相同的记忆效果),以??及原子的添加、递增和递减方法。AtomicInteger表面上类似于扩展的Counter类,但在竞争事件中提供了更高的可伸缩性,因为它直接利用了硬件对并发的支持。为什么会有原子类CAS:CompareandSwap,即比较和交换。jdk5增加了concurrent包java.util.concurrent.*,下面的类使用CAS算法实现了区别于synchronous同步锁的乐观锁。在JDK5之前,Java语言依赖于synchronized关键字来保证同步。这是排他锁和悲观锁。如果同一个变量要被多个线程访问,可以使用本包中的类AtomicBooleanAtomicIntegerAtomicLongAtomicReferenceCAS无锁模式。什么是CASCAS:CompareandSwap,即比较和交换。jdk5增加了concurrent包java.util.concurrent.*,下面的类使用CAS算法实现了区别于synchronous同步锁的乐观锁。在JDK5之前,Java语言依赖于synchronized关键字来保证同步。这是排他锁和悲观锁。CAS算法理解(1)与锁相比,使用比较交换(以下简称CAS)会使程序看起来更复杂。但由于其非阻塞的特性,天生就免疫死锁问题,线程间的交互也远小于基于锁的。更重要的是,使用无锁方式完全没有锁竞争带来的系统开销,也没有线程间频繁调度带来的开销。因此,它比基于锁的方法具有更好的性能。(2)无锁的好处:第一,在高并发的情况下,比有锁的程序有更好的性能;其次,它天生不会死锁。仅凭这两个优点,就值得我们冒险尝试使用无锁并发。(3)CAS算法的过程如下:它包含三个参数CAS(V,E,N):V代表要更新的变量,E代表期望值,N代表新值。只有当V的值等于E的值时,V的值才会被设置为N。如果V的值与E的值不同,说明其他线程已经进行了更新,当前线程线程什么都不做。最后,CAS返回V的当前真值。(4)CAS操作是以乐观的态度进行的,它始终认为自己可以成功完成操作。当多个线程同时使用CAS操作一个变量时,只有一个线程获胜并更新成功,其余的都会失败。失败的线程不会被挂起,只是通知失败,允许重试,当然也允许失败的线程放弃操作。基于这个原理,即使CAS操作没有锁,也可以检测到其他线程对当前线程的干扰,并进行适当的处??理。(5)简单的说,CAS要求你额外给出一个期望值,也就是你认为这个变量现在应该是什么样子。如果这个变量不是你想的那样,它已经被别人修改了。只需重新阅读并尝试再次修改它。(6)在硬件层面,大多数现代处理器已经支持原子CAS指令。在JDK5.0之后,虚拟机可以使用这条指令来实现并发操作和并发数据结构,而这样的操作在虚拟机中可以说是无处不在。常用的原子类Java中的原子操作类大致可以分为四类:原子更新基本类型、原子更新数组类型、原子更新引用类型、原子更新属性类型。这些原子类都使用了无锁的概念,有的地方直接使用线程安全类型的CAS操作。AtomicBooleanAtomicIntegerAtomicLongAtomicReferencepublicclassTest0001implementsRunnable{privatestaticIntegercount=1;privatestaticAtomicIntegeratomic=newAtomicInteger();@Overridepublicvoidrun(){while(true){intcount=getCountAtomic();System.out.println(count);5}}(count>brak>e1publicsynchronizedIntegergetCount(){try{Thread.sleep(50);}catch(Exceptione){//TODO:handleexception}returncount++;}publicIntegergetCountAtomic(){try{Thread.sleep(50);}catch(Exceptione){//TODO:handleexception}returnatomic.incrementAndGet();}publicstaticvoidmain(String[]args){Test0001test0001=newTest0001();Threadt1=newThread(test0001);Threadt2=newThread(test0001);t1.start();t2.start();}}CAS(乐观锁算法)的基本假设CAS比较交换的伪代码可以表示为:do{备份旧数据;根据旧数据构建新数据;}while(!CAS(内存地址,备份旧数据,新数据)(上图的解释:CPU更新了一个值,但是如果你想改变这个值我s不再是原来的值,操作会失败,因为很明显是其他操作先改变了值。)意思是当两个比较时,如果相等,则证明共享数据没有被修改,替换给它一个新的值,然后继续运行;如果不等于,则说明共享数据已经被修改,放弃已经做过的操作,然后重新执行之前的操作Operation。不难看出,CAS操作是基于共享数据不会被修改的假设,采用了类似于数据库的commit-retry的模式。当同步冲突的机会很少时,这种假设可以带来巨大的性能提升。publicfinalintgetAndInt(Objecto,longoffset,intdelta){intv;do{v=getIntVolatile(o,offset);}while(!compareAndSwapInt(o,offset,v,v+delta));returnv;}/***Atomicallyincrementsbyonethecurrentvalue.**@returntheupdatedvalue*/publicfinalintincrementAndGet(){for(;;){//获取当前值intcurrent=get();//设置期望值intnext=current+1;//调用Native方法compareAndSet,进行CAS操作if(compareAndSet(current,next))//成功后才会返回期望值,否则无线循环returnnext;}}CAS缺点CAS有一个很明显的问题,就是ABA问题。问题:如果变量V在第一次被读取时是A,准备赋值时检查仍然是A,是否说明它的值没有被其他线程修改过?如果已经改成B,再改回A,那么CAS操作就会误认为从来没有被修改过。针对这种情况,java并发包提供了一个带标记的原子引用类AtomicStampedReference,可以通过控制变量值的版本来保证CAS的正确性。分布式锁如果要保证不同jvm中的数据同步,使用分布式锁技术。有数据库实现,缓存实现,Zookeeper分布式锁