Lock接口与读写锁(lock 和 synchronized 的比较)

本文深入探讨Java中的锁机制,包括ReentrantLock与synchronized的区别、读写锁的应用场景及其实现方式。通过具体实例,展示了如何利用锁来解决多线程环境下的数据一致性问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

出现:JDK1.5
实现类:
ReentrantLock(重入锁、手工锁)
ReentrantReadWriteLock.ReadLock ReentrantReadWriteLock.WriteLock
主要方法:
lock() unlock()
使用:
TestLock.java
1
public class TestLock implement Runnable{
2
3
    ReentrantLock lock = new ReentrantLock();
4
5
    @Override
6
    public void run(){
7
        
8
            try{
9
                //加锁
10
                lock.lock();
11
                //执行同步代码
12
            }
13
            finally{
14
                //释放锁
15
                lock.unlock();
16
            }
17
18
}
和synchronized互斥锁的比较:
(1)synchronized关键字修饰的代码是完全互斥的,而Lock更加灵活,可以实现多线程同时读共享数据(读锁和读锁不互斥),但读锁和写锁互斥。写锁和写锁互斥(读写锁)。
(2)ReentrainLock有tryLock方法:
1
public class ReentrainLockTest{
2
    //获取锁
3
    Lock lock = new ReentrainLock();
4
    //线程1的方法:获取锁
5
    void m1(){
6
        lock.lock();//锁定
7
        try{
8
            for(int i=0;i<10;i++){//锁定10秒
9
                TimeUtil.SECONDS.sleep(1);//sleep 1秒
10
            }
11
        }catch(InterruptException e){
12
            e.printStackTrace();
13
        }finally{
14
            lock.unlock();//10秒后释放锁
15
        }
16
    }
17
    //线程2的方法
18
    void m2(){
19
        boolean locked = lock.tryLcok();//尝试锁定,如果返回为false,则获取锁失败,但程序继续执行。
20
        Systerm.out.println("是否获得锁:" + locked);
21
        if(locked) lock.unlock();//如果获得锁,则解锁
22
    }
23
    
24
    public static void main(String[] args){
25
        //开启两个线程,线程1调用m1方法,线程2调用m2方法
26
    } 
27
}
(3)lockInterruptibly方法持有的锁,如果没获取到锁可被其它线程打断
1
public class{
2
    public static void main(Stirng[] args){
3
        Lock lock = new ReentrainLock();
4
        //1.创建t1线程,一直持有锁
5
        Thread t1 = new Thread(()-> {
6
            lock.lock();
7
            try{
8
                while(true){//一直持有锁
9
                    TimeUtils.SECOND.sleep(1);
10
                }
11
            }catch(InterruptException e){
12
                e.printStackTrace();
13
            }fainally{
14
                lock.unlock();//释放锁
15
            }
16
        });
17
        t1.start();
18
        
19
        //2.启动t2线程,尝试获取锁,但是一直获取失败(t1线程一直持有锁)
20
        Tread t2 = new Thread(()->{
21
            try{
22
                boolean isLocked = lock.tryLock();
23
                lock.lockInterruptibly();//表示可以对其它线程的interrupt方法做出响应
24
                System.out.println("t2线程启动!");
25
            }catch(InterruptException e){
26
                e.printStackTrace();
27
            }finally{
28
                if(isLocked) lock.unlock();
29
            }
30
            
31
        });
32
        t2.start();
33
        
34
        //3.主线程1秒后打断t2线程的等待锁状态,t2线程结束,抛出异常
35
        try{
36
            timeUtils.SECONDS.sleep(1)
37
        }catch(InterruptException e){
38
            e.printStackTrace();
39
        }
40
        t2.interrupt();
41
    }
42
}
(4)reentrianLock可以指定公平锁(synchronized为不公平锁)
synchronized在锁释放时,所有线程都在竞争锁(不确定谁能拿到锁),很可能有的线程一次都拿不到,不公平
reentrianLock可以在锁竞争时,判断哪个线程等待的时间长,就先让哪个线程获取锁
1
Lock lock = new ReentrainLock(true);//传入true为公平锁,默认为非公平锁

读写锁接口:
ReadWriteLock
实现类:
ReentranReadWriteLock
方法:
readLock、writeLock(返回Lock对象)
使用:
1
ReadWriteLock rwl = new ReenTranReadWriteLock();
2
rwl.readLock().lock();//获取读锁
3
rwl.readLock().unLock();//释放读锁
4
5
rwl.writeLock().lock();//获取写锁
6
rwl.writeLock().unLock();//释放写锁
例子:用户从银行读取余额(读锁,多用户同时读一个账号)。用户从银行存取钱(写锁,每次只能一个用户从同一个账号存取钱)

用户账户类:MyAccount.java
1
public class MyCount {  
2
         private String oid;         //账号  
3
         private int cash;             //账户余额  
4
  
5
         MyCount(String oid, int cash) {  
6
                 this.oid = oid;  
7
                 this.cash = cash;  
8
         }  
9
  
10
         public String getOid() {  
11
                 return oid;  
12
         }  
13
  
14
         public void setOid(String oid) {  
15
                 this.oid = oid;  
16
         }  
17
  
18
         public int getCash() {  
19
                 return cash;  
20
         }  
21
  
22
         public void setCash(int cash) {  
23
                 this.cash = cash;  
24
         }  
25
  
26
         @Override  
27
         public String toString() {  
28
                 return "MyCount{" +  
29
                                 "oid='" + oid + '\'' +  
30
                                 ", cash=" + cash +  
31
                                 '}';  
32
         }  
33
}  

线程类(多用户同时访问一个账号):UserThread.java
1
public class UserThread implements Runnable {  
2
         private String name;                 //用户名  
3
         private MyCount myCount;         //所要操作的账户  
4
         private int iocash;                 //操作的金额,当然有正负之分了  
5
         private ReadWriteLock myLock;                 //执行操作所需的锁对象  
6
         private boolean ischeck;         //是否查询  
7
  
8
         public UserThread(String name, MyCount myCount, int iocash, ReadWriteLock myLock, boolean ischeck) {  
9
                 this.name = name;  
10
                 this.myCount = myCount;  
11
                 this.iocash = iocash;  
12
                 this.myLock = myLock;  
13
                 this.ischeck = ischeck;//是否查看余额  
14
         }  
15
  
16
         public void run() {  
17
                 if (ischeck) {  //查看余额
18
                         //获取读锁  
19
                         myLock.readLock().lock();  
20
                         System.out.println("读:" + name + "正在查询" + myCount + "账户,当前金额为" + myCount.getCash());  
21
                         //释放读锁  
22
                         myLock.readLock().unlock();  
23
                 } else {  //存取钱
24
                         //获取写锁  
25
                         myLock.writeLock().lock();  
26
                         //执行现金业务  
27
                         System.out.println("写:" + name + "正在操作" + myCount + "账户,金额为" + iocash +",当前金额为" + myCount.getCash());  
28
                         myCount.setCash(myCount.getCash() + iocash);  
29
                         System.out.println("写:" + name + "操作" + myCount + "账户成功,金额为" + iocash +",当前金额为" + myCount.getCash());  
30
                         //释放写锁  
31
                         myLock.writeLock().unlock();  
32
                 }  
33
         }  
34
}  

测试类:Test.java
1
public class Test {  
2
         public static void main(String[] args) {  
3
                 //创建并发访问的账户  
4
                 MyCount myCount = new MyCount("95599200901215522", 10000);  
5
                 //创建一个锁对象  
6
                 ReadWriteLock lock = new ReentrantReadWriteLock(false);  
7
                 //创建一个线程池  
8
                 ExecutorService pool = Executors.newFixedThreadPool(2);  
9
                 //创建一些并发访问用户,一个信用卡,存的存,取的取,好热闹啊  
10
                 User u1 = new User("Mike", myCount, -4000, lock, false);  
11
                 User u2 = new User("Mike's Dad", myCount, 6000, lock, false);  
12
                 User u3 = new User("Mike's Brother", myCount, -8000, lock, false);  
13
                 User u4 = new User("Mike's Mom", myCount, 800, lock, false);  
14
                 User u5 = new User("Mike's son", myCount, 0, lock, true);  
15
                 //在线程池中执行各个用户的操作  
16
                 pool.execute(u1);  
17
                 pool.execute(u2);  
18
                 pool.execute(u3);  
19
                 pool.execute(u4);  
20
                 pool.execute(u5);  
21
                 //关闭线程池  
22
                 pool.shutdown();  
23
         }  
24
}  
锁升级和降级原则:
读锁不可升级成写锁(读锁释放前不可以加写锁)

写锁可以降级成读锁(写锁释放前可以加读锁)

官方文档读写锁例子:
1
 class CachedData {
2
   Object data;
3
   volatile boolean cacheValid;
4
   final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
5
6
   void processCachedData() {
7
     rwl.readLock().lock();
8
     if (!cacheValid) {//第一重检测,没有缓存数据,释放读锁,加写锁写数据
9
        // 在写锁前必须释放读锁
10
        rwl.readLock().unlock();
11
        rwl.writeLock().lock();
12
        try {
13
          //第二重检测,写数据前再次确保缓存没有数据,防止在第10行释放了读锁后,被其它线程的写锁拿到,往缓存里写了数据
14
          if (!cacheValid) {
15
            data = ...//数据库写入修改操作
16
            cacheValid = true;
17
          }
18
          // 锁降级,在写锁里面先上读锁,把写锁降级成读锁
19
          rwl.readLock().lock();
20
        } finally {
21
          rwl.writeLock().unlock(); // 在读锁状态下释放写锁
22
        }
23
     }
24
25
     try {
26
       use(data);
27
     } finally {
28
       rwl.readLock().unlock();
29
     }
30
   }
31
 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值