当前位置: 首页 > 后端技术 > Java

Java线程同步机制

时间:2023-04-01 19:09:06 Java

线程同步机制是一种保证线程安全,协调线程直接数据访问的机制。Java提供的线程同步机制包括:lockvolatile关键字final关键字static关键字相关API(如Object.转换为串行访问,即一个共享数据一次只能被一个线程访问。我们通常听说有使用的锁有很多种:公平锁/非公平锁、可重入锁/不可重入锁、共享锁/排他锁、乐观锁/悲观锁、分段锁、偏向锁/轻量级锁/重量级锁、自旋锁。其实这些都是锁在不同维度或者从锁优化角度的称呼,JVM把锁分为两种:隐式锁(内部锁)和显式锁,所谓显式和隐式是指用户是否应该使用时手动编写代码获取和释放锁隐式锁/内部锁:通过synchronized关键字实现显示锁:通过Lock接口的实现类实现,主要有ReentrantLock、StampedLock、ReadWriteLock、ReentrantReadWriteLock等.当使用synchronized关键字时,我们不需要编写其他代码程序就可以获取和释放锁。那是因为synchronized代码块执行时,系统会自动让程序释放占用的锁,这个锁是系统维护的。如果不是逻辑问题,就不会出现死锁。在使用Lock时,我们需要手动获取和释放锁。如果不释放锁,可能会导致死锁。synchronized(隐式锁)synchronized可以修改代码块或方法。个人理解,加锁同一个变量的代码块或方法共享同一个锁,同时共享同一个锁的代码块或方法只能被一个线程访问。只要明白了这一点,所谓的方法锁(其实就是对象锁)、对象锁、类锁就很好理解了。同步代码块//1。锁定非静态变量。锁定相同变量的代码块共享相同的锁。非静态变量不被其他线程共享,所以调用同一个方法的多个实例不会受到这个锁的影响。私有对象锁=新对象();publicvoidmethod(){synchronized(lock){//code...}}//2.锁定此对象。锁定这个的所有代码块共享同一个锁。同样,调用这些方法的多个实例也不会受到锁的影响。同步(this){//代码...}//3。锁定静态变量。由于JVM方法区静态变量只有一份,而方法区是所有线程共享的,所以给静态变量加锁就相当于类锁。私有静态对象锁=新对象();publicvoidmethod(){synchronized(lock){//code...}}//4.锁定xxx.class。即类锁,所有线程共享。synchronized(XXX.class){//code...}同步方法//1.锁定非静态方法。相当于锁对象就是this。publicsynchronizedvoidmethod(){//代码...}//2。锁定静态方法。相当于一个类锁。publicstaticsynchronizedvoidmethod(){//code...}Lock(显式锁)synchronized关键字的锁由JVM层实现。JDK5以后,java.util.concurrentjava.util.concurrent.locks包中有显式锁,即Lock和ReadWriteLock。常见的实现类有:ReentrantLock(Lock实现类)ReentrantReadWriteLock(ReadWriteLock实现类)Lock接口有以下方法://获取锁voidlock()//如果当前线程没有被中断,获取锁并响应中断voidlockInterruptibly()//返回一个绑定到这个Lock实例的新Condition实例ConditionnewCondition()//调用时只有在锁空闲时才获取锁,可以响应中断booleantryLock()//如果锁在thegiven如果等待时间空闲且当前线程没有被中断,则获取锁booleantryLock(longtime,TimeUnitunit)//释放锁voidunlock()ReadWriteLock接口有两个方法://返回锁Lock用于读操作readLock()//返回锁用于写操作的锁writeLock()ReentrantLocklock(),tryLock(),tryLock(longtime,TimeUnitunit)和lockInterruptibly()都是用来获取锁的。unLock()方法用于释放锁。newCondition()返回绑定到此Lock的新Condition实例以进行线程间协作。其中,这四种获取锁的方法有什么区别呢?参考文章《Java锁Lock接口详解》。lock()和unlock()的用法如下:Locklock=newReentrantLock();lock.lock();//获取锁try{//处理任务}catch(Exceptionex){}finally{lock.开锁();//释放锁}ReentrantReadWriteLockReadWriteLock维护了一对相关的锁,一个用于只读操作,一个用于写操作。只要没有写者,读锁可以由多个读线程并发持有,而写锁是互斥的。私有对象数据=空;//共享数据,只有一个线程可以写入数据,但多个线程可以同时读取数据。ReadWriteLocklock=newReentrantReadWriteLock();//读取数据publicvoidget(){lock.readLock().lock();//添加读锁try{//处理任务}catch(InterruptedExceptione){e.printStackTrace();}最后{lock.readLock().unlock();//释放读锁}}//写数据publicvoidput(Objectdata){lock.writeLock().lock();//writelocktry{//处理任务}catch(InterruptedExceptione){e.printStackTrace();}最后{lock.writeLock().unlock();//释放写锁}}锁的类型/typefair/unfairlock可以是Reentrant/non-reentrantlockshared/exclusivelockoptimisticlocksynchronizedunfairlockreentrantlockexclusivelockpessimisticlockReentrantLock都支持可重入锁exclusivelock悲观锁ReentrantReadWriteLock都支持可重入锁读锁-共享,写锁-独占悲观锁公平锁和非公平锁、可重入锁和不可重入锁、共享锁和排它锁、乐观锁和悲观锁有什么区别?请参考文章《Java锁Lock的种类》。volatile关键字是线程同步的轻量级实现,性能优于synchronized。volatile只能修改变量,可以解决变量在多线程中的可见性。使用方法如下:privatestaticvolatileintcount;其他线程同步机制除了锁机制和volatile关键字外,Java中还有一些实现线程同步的方法。final关键字的原理是通过禁止CPU的指令集重新排序来提供线程可见性,从而保证线程同步。当一个对象被释放给其他线程时,该对象的所有final字段都被初始化,即其他线程读取对应字段的初始值,而不是默认值。static关键字确保即使不使用其他线程同步机制,线程也可以读取类静态变量的初始值,但这种可见性仅限于变量的初始读取。参考多线程与高并发(二)volatile关键字的类型JavaLock锁Java锁总结(这篇文章太深入)Java并发中显式锁和隐式锁的区别对象锁和类锁的区别javaLock锁接口详解