java中锁的详解

简介: java中锁的详解

锁的种类

Java中的锁是一种同步机制,用于保护共享资源,防止多个线程同时访问。在Java中,锁可以分为不同的类型,每种类型都有其特定的优缺点和适用场景。

1.重量级锁(Heavy Lock):重量级锁是一种互斥锁,它提供了最高的线程安全性。当一个线程获得了重量级锁时,其他线程必须等待该线程释放锁才能获得锁。因此,重量级锁的开销较大,可能会导致线程阻塞和性能下降。但是,由于重量级锁提供了最高的线程安全性,所以在需要确保数据完整性和一致性的情况下,重量级锁是非常有用的。

2.轻量级锁(Lightweight Lock):轻量级锁是一种比重量级锁更轻量级的同步机制。它使用CAS操作来实现互斥访问,并且可以在没有竞争的情况下快速获取锁。因此,轻量级锁的开销较小,可以提高并发性能。但是,由于轻量级锁不是互斥的,所以如果多个线程同时尝试获取锁,则可能导致数据不一致的问题。

3.互斥锁(Mutex Lock):互斥锁是一种基本的同步机制,它用于保护共享资源,防止多个线程同时访问。互斥锁提供了原子性操作,即在任何时候只能有一个线程访问共享资源。Java中的synchronized关键字就是一种互斥锁。

4.读写锁(Read-Write Lock):读写锁允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。读写锁可以提高并发性能,特别是在读操作比写操作频繁的情况下。Java中的ReentrantReadWriteLock就是一种读写锁。

5.可重入锁(Reentrant Lock):可重入锁允许同一个线程多次获取同一个锁,这意味着线程可以在执行完同步代码后再次获取该锁而不需要重新竞争。可重入锁通常用于递归调用等情况。Java中的ReentrantLock就是一种可重入锁。

6.自旋锁(Spin Lock):自旋锁是一种基于忙等待的同步机制,它在获取锁时会一直循环等待,直到获取到锁为止。自旋锁的开销较大,因为它会占用大量的CPU时间。但是,自旋锁可以提供极高的并发性能,特别是在竞争激烈的情况下。

7.乐观锁(Optimistic Lock):乐观锁是一种不涉及实际锁定的同步机制,它通过版本号或哈希值来检查共享资源是否被其他线程修改过。如果没有修改,则可以更新共享资源。乐观锁可以提高并发性能,但可能会导致数据不一致的问题。Java中的CAS操作就是一种乐观锁实现方式。

8.悲观锁(Pessimistic Lock):悲观锁是一种实际锁定的同步机制,它在访问共享资源之前先将其锁定。悲观锁可以保证数据的一致性,但会导致并发性能下降。Java中的synchronized关键字就是一种悲观锁实现方式。

以下是Java中各种锁的实现代码示例:

1. synchronized关键字实现互斥锁[悲观锁]:
public class SynchronizedDemo { 
    private int count = 0; 
    public synchronized void increment() { 
        count++; 
    } 
    public int getCount() { 
        return count; 
    } 
} 
2. ReentrantLock类实现可重入锁:
import java.util.concurrent.locks.ReentrantLock; 
public class ReentrantLockDemo { 
    private final ReentrantLock lock = new ReentrantLock(); 
    public void doSomething() { 
        lock.lock(); // 可重入锁必须先获取到锁才能执行同步代码,因此需要使用lock()方法获取锁 
        try { 
            // 同步代码块 
        } finally { 
            lock.unlock(); // 在finally块中释放锁,即使发生异常也需要释放锁 
        } 
    } 
} 
3. ReadWriteLock类实现读写锁:
import java.util.concurrent.locks.ReadWriteLock; 
import java.util.concurrent.locks.ReentrantReadWriteLock; 
public class ReadWriteLockDemo { 
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); 
    public void readData() { 
        rwl.readLock().lock(); // 只允许读取操作获取到读锁,因此需要使用readLock()方法获取读锁 
        try { 
            // 读取共享资源的操作 
        } finally { 
            rwl.readLock().unlock(); // 在finally块中释放读锁,即使发生异常也需要释放读锁 
        } 
    } 
    public void writeData() { 
        rwl.writeLock().lock(); // 只允许写入操作获取到写锁,因此需要使用writeLock()方法获取写锁 
        try { 
            // 写入共享资源的操作 
        } finally { 
            rwl.writeLock().unlock(); // 在finally块中释放写锁,即使发生异常也需要释放写锁 
        } 
    } 
} 
4.自旋锁示例代码,它使用Java中的ReentrantLock类实现:
import java.util.concurrent.locks.ReentrantLock;
public class SpinLockDemo {
    private final ReentrantLock lock = new ReentrantLock();
    public void doSomething() {
        while (true) {
            try {
                lock.lockInterruptibly(); // 尝试获取锁,如果获取失败则进入循环等待
                break;
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // 抛出异常时中断当前线程
            }
        }
        try {
            // 同步代码块
        } finally {
            lock.unlock(); // 在finally块中释放锁,即使发生异常也需要释放锁
        }
    }
}

在上面的示例代码中,doSomething()方法会一直循环等待获取锁。当它尝试获取锁时,如果获取失败,则会抛出InterruptedException异常并中断当前线程。一旦它成功获取到锁,就可以执行同步代码块了。最后,无论同步代码块是否正常执行,doSomething()方法都会在finally块中释放锁。这样可以确保即使发生异常,也不会导致锁没有被正确释放的问题。

5. 乐观锁示例代码,它使用Java中的CAS操作实现:
import java.util.concurrent.atomic.AtomicInteger;
public class OptimisticLockDemo {
    private final AtomicInteger count = new AtomicInteger(0);
    public void increment() {
        int currentCount = count.get();
        while (true) {
            int nextCount = currentCount + 1;
            if (count.compareAndSet(currentCount, nextCount)) {
                break;
            } else {
                currentCount = nextCount;
            }
        }
    }
}

在上面的示例代码中,increment()方法会不断尝试将共享资源的计数器从当前值加1。如果成功更新了计数器的值,则说明没有其他线程在同一时间点对共享资源进行了修改,因此可以安全地进行同步操作。否则,increment()方法会继续循环等待,直到获取到锁为止。这样可以确保即使发生竞争条件,也不会导致数据的不一致性问题。


目录
打赏
0
1
1
0
11
分享
相关文章
|
10月前
|
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
111 2
深入理解Java锁升级:无锁 → 偏向锁 → 轻量级锁 → 重量级锁(图解+史上最全)
锁状态bits1bit是否是偏向锁2bit锁标志位无锁状态对象的hashCode001偏向锁线程ID101轻量级锁指向栈中锁记录的指针000重量级锁指向互斥量的指针010尼恩提示,讲完 如减少锁粒度、锁粗化、关闭偏向锁(-XX:-UseBiasedLocking)等优化手段 , 可以得到 120分了。如减少锁粒度、锁粗化、关闭偏向锁(-XX:-UseBiasedLocking)等‌。JVM锁的膨胀、锁的内存结构变化相关的面试题,是非常常见的面试题。也是核心面试题。
深入理解Java锁升级:无锁 → 偏向锁 → 轻量级锁 → 重量级锁(图解+史上最全)
|
8月前
|
Java 中的锁是什么意思,有哪些分类?
在Java多线程编程中,锁用于控制多个线程对共享资源的访问,确保数据一致性和正确性。本文探讨锁的概念、作用及分类,包括乐观锁与悲观锁、自旋锁与适应性自旋锁、公平锁与非公平锁、可重入锁和读写锁,同时提供使用锁时的注意事项,帮助开发者提高程序性能和稳定性。
324 3
java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁
本文介绍了几种常见的锁机制,包括公平锁与非公平锁、可重入锁与不可重入锁、自旋锁以及读写锁和互斥锁。公平锁按申请顺序分配锁,而非公平锁允许插队。可重入锁允许线程多次获取同一锁,避免死锁。自旋锁通过循环尝试获取锁,减少上下文切换开销。读写锁区分读锁和写锁,提高并发性能。文章还提供了相关代码示例,帮助理解这些锁的实现和使用场景。
231 4
java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁
|
9月前
|
Java 中锁的主要类型
【10月更文挑战第10天】
244 59
java的synchronized有几种加锁方式
Java的 `synchronized`通过上述三种加锁方式,为开发者提供了从粗粒度到细粒度的并发控制能力,满足了不同场景下的线程安全需求。合理选择加锁方式对于提升程序的并发性能和正确性至关重要,开发者应根据实际应用场景的特性和性能要求来决定使用哪种加锁策略。
133 0
【锁的艺术】StampedLock:Java并发编程的新武器!
【8月更文挑战第24天】`StampedLock`, 作为 Java 8 引入的新特性,为开发者提供了一种相较于传统 `ReentrantReadWriteLock` 更高效且灵活的锁机制。它属于 `java.util.concurrent.locks` 包,主要特点包括乐观读锁,这在多读少写的场景下能显著提升性能。
92 1
Java中到底有哪些锁
【9月更文挑战第24天】在Java中,锁主要分为乐观锁与悲观锁、自旋锁与自适应自旋锁、公平锁与非公平锁、可重入锁以及独享锁与共享锁。乐观锁适用于读多写少场景,通过版本号或CAS算法实现;悲观锁适用于写多读少场景,通过加锁保证数据一致性。自旋锁与自适应自旋锁通过循环等待减少线程挂起和恢复的开销,适用于锁持有时间短的场景。公平锁按请求顺序获取锁,适合等待敏感场景;非公平锁性能更高,适合频繁加解锁场景。可重入锁支持同一线程多次获取,避免死锁;独享锁与共享锁分别用于独占和并发读场景。
244 3
JAVA并发编程-一文看懂全部锁机制
曾几何时,面试官问:java都有哪些锁?小白,一脸无辜:用过的有synchronized,其他不清楚。面试官:回去等通知! 今天我们庖丁解牛说说,各种锁有什么区别、什么场景可以用,通俗直白的分析,让小白再也不怕面试官八股文拷打。
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等