什么是锁?有几种锁?怎么用锁?

本文深入探讨Java中的锁机制,包括synchronized、ReentrantLock及ReadWriteLock的特性与使用方法,解析公平与非公平锁的区别,以及如何在多线程环境下有效控制资源访问。

生活中,我们在门上可以见到锁,在手机上可以见到锁,在电脑上,汽车上,保险柜上......等等,可见锁在我们生活中是一个随处可件的物品,但我们今天要讲的是,java中的锁。本篇文章仅博主个人观点,仅供参考,共同进步,感谢博友支持。

什么是锁?

将某种资源私有化的一种物品,没错java里面的锁也是这种特性,它可以让某个方法,某个变量或某个通道,在某个时刻下只能被一个线程占用。只有当这个锁释放了,另外的线程才可以使用。例子:上厕所,一个同事上厕所把门锁上,这个时候厕所就被上锁了,别人要想进去,只能等这位同事释放锁后出来,其他同事才可进入。这个厕所可以看成是临界区,同事就是线程。

临界区?

多个线程共享同一个资源的情况,为了防止出现数据不一致情况的发生,人们引入了临界区的概念。临界区是一个用来访问共享资源的代码块,同一时间内只运行一个线程进入。

有几种锁?

java体系中有几种锁呢?

  • synchronized(不公平)
  • ReentrantLock 重入锁 (可公平可不公平)
  • ReadWriteLock 读写锁(不公平)

公平?不公平

公平锁会按照时间的先后顺序来给予锁

不公平锁在等待队列随机挑选一个来给予锁

例子:银行办理业务排队,一堆人要办业务,都围在窗口前,业务员也顾不得前后,顺便就找个人办理,这样是不公平的,而公平的则是先到先办理后到后办理

怎么用锁?

要想知道怎么用锁需要清楚每个锁的特点,才能对症下药。

例子:在不带锁的情况下,线程同时进入方法,如果业务处理的是减库存又或者是加减金额,就会出现由高并发下带来的问题,导致数据错误混乱

public class SynchronizedDemo {

    public static void addAmount() {
        System.out.println("进入方法");
        try {
            // 模拟业务处理
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("退出方法");
    }

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            new Thread(() -> addAmount()).start();
        }
    }
}
结果:
进入方法
进入方法
进入方法
退出方法
退出方法
退出方法

synchronized

特性:可重入,可见性

缺点:效率比较低,不够灵活,不可中断,无法感知获取锁

package com.zqh.www;

public class SynchronizedDemo {

    public synchronized static void addAmount() {
        System.out.println("进入方法");
        try {
            // 模拟业务处理
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("退出方法");
    }

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            new Thread(() -> addAmount()).start();
        }
    }
}
进入方法
退出方法
进入方法
退出方法
进入方法
退出方法

ReentrantLock:synchronized的增强版

特性:效率较高,灵活,可感知获取锁,可公平可非公平,可中断

缺点:需在finally释放锁

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo {

    private static ReentrantLock reentrantLock = new ReentrantLock();

    public static void addAmount() {
        reentrantLock.lock();
        try {
            System.out.println("进入方法");
            try {
                // 模拟业务处理
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("退出方法");
        } finally {
            reentrantLock.unlock();
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            new Thread(() -> addAmount()).start();
        }
    }
}
进入方法
退出方法
进入方法
退出方法
进入方法
退出方法

ReadWriteLock

特性:效率较高,灵活,可感知获取锁,读写分离控制(读多写少),读读并行,读写串行,写写串行

缺点:需在finally释放锁

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockDemo {
    private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    private static Lock readLock = reentrantReadWriteLock.readLock();
    private static Lock writeLock = reentrantReadWriteLock.writeLock();
    private static volatile int value = 0;

    public static void readAmount() {
        readLock.lock();
        try {
            System.out.println("查看金额:" + value);
            try {
                // 模拟业务处理
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } finally {
            readLock.unlock();
        }
    }

    public static void addAmount() {
        writeLock.lock();
        try {
            System.out.println("进入方法");
            try {
                // 模拟业务处理
                ++value;
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("退出方法");
        } finally {
            writeLock.unlock();
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            new Thread(() -> readAmount()).start();
        }
        for (int i = 0; i < 3; i++) {
            new Thread(() -> addAmount()).start();
        }
    }
}

 

### 的概念及其作用 是一种用于管理对共享资源访问的工具,在多线程环境中,当多个线程试图同时修改同一份数据时,可能会引发数据不一致的问题。为了防止这种情况发生,程序设计者可以利用来控制哪些线程可以在特定时间范围内访问这些资源。 通过定一段代码区域或对象,其他尝试进入该临界区的线程会被阻塞直到当前持有的线程完成操作并释放为止。这有助于维护系统的稳定性和可靠性,同时也保障了数据的一致性[^1]。 ### Java 中的各种类型及应用场景 #### 1. `synchronized` 关键字 `synchronized` 是最简单的同步机制之一,它可以用来修饰方法或者作为代码块的一部分使用。其主要特点是简单易用,并且内置支持监视器(Monitor),即每次只有一个线程能执行被此关键字保护的方法体内的语句;一旦某个线程获得了,则其它任何想要获得相同的线程都必须等待前者退出临界区之后才能继续运行。 ##### 场景: 适用于那些不需要复杂功能的小型应用程序或者是对于性能要求不是特别高的地方。由于它的开销相对较大,因此不适合高频率竞争的情况。 ```java public class Counter { private int count; public synchronized void increment() { // 使用 synchronized 同步整个方法 this.count++; } } ``` #### 2. `ReentrantLock` (可重入) 相比于 `synchronized` 提供更灵活的功能集,比如允许设置公平策略、提供条件队列等功能。此外还可以显式地调用 lock/unlock 方法来进行加解动作而不是依赖于 JVM 自动处理。 ##### 特征: - 支持非阻塞式的 tryLock() - 能够响应中断请求 unlockInterruptibly() - 具备超时获取的能力 tryLock(long timeout, TimeUnit unit) ##### 应用场景: 适合需要更加精细粒度控制的应用场合,特别是当业务逻辑较为复杂时可以选择此类结构以提升效率和灵活性。 ```java import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class SharedResource { final Lock lock = new ReentrantLock(); public void accessSharedData(){ lock.lock(); // 显式上 try{ // 访问共享的数据... }finally{ lock.unlock(); // 确保最终会解 } } } ``` #### 3. `ReadWriteLock`(读写) 这种类型的允许多个读者同时存在但是不允许有写者的同时存在。也就是说如果现在有一个线程正在读取某项资源那么其他的只读线程也可以加入进来一起工作而不会造成冲突;但如果此时来了一个打算对该资源做更改的操作则所有的阅读活动都要暂停直至更新完毕再恢复正常的读流程。 ##### 主要优势在于提高了吞吐量特别是在大量读少部分写的模式下表现尤为明显。 ```java import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; class Cache<K,V> implements ConcurrentMap<K,V>{ private final Map<K,V> m = new HashMap<>(); private final ReadWriteLock rwl = new ReentrantReadWriteLock(); @Override public V get(Object key){ rwl.readLock().lock(); try{return m.get(key);} finally{rwl.readLock().unlock();} } @Override public V put(K key, V value){ rwl.writeLock().lock(); try{return m.put(key,value);} finally{rwl.writeLock().unlock();} } } ``` #### 4. 乐观 (`OptimisticLock`) 不同于传统意义上的互斥,乐观并不真正阻止其他事务的发生而是假设大多数情况下不会有冲突所以先让它们自由发展等到提交阶段再去验证是否有违反预期的行为发生如果有就回滚重新计算否则正常结束交易过程这种方式减少了不必要的等待从而提升了整体性能。 ##### 常见实现方式是在数据库表中增加版本号字段 version 或 timestamp 类型的时间戳每当记录发生变化的时候都会自动递增这个值以此标记最新的状态信息客户端在发起更新之前会携带旧版本的信息服务器端接收到后对比两者是否相匹配如果不符说明期间有人做了改动于是拒绝此次变更反之则接受并将新版本号返回给前端保存起来以便下次比较之用。 ```sql UPDATE table_name SET column=value,version=version+1 WHERE id=? AND version=? ``` #### 5. 悲观 (`PessimisticLock`) 与乐观相反的是悲观认为并发环境下的每一次交互都有可能发生碰撞因而采取预防措施提前占用必要的资源确保独占使用权在整个过程中都不会受到外界干扰虽然这种方法安全性较高但却牺牲了一定程度上的并发能力因为长时间保持定状态容易引起死等问题所以在实际开发当中应该谨慎选用除非确实有必要才考虑采用这样的方案。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值