线程锁

本文深入探讨Java1.5引入的Lock接口及其实现ReentrantLock,解析其在多线程环境下的应用,包括死锁的产生及避免策略。

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

同步锁(Lock)
Java1.5开始,Java提供了一种功能更强大的线程同步机制—-通过显示定义同步锁对象来实现同步,在这种机制下,同步锁使用Lock对象充当。
Lock有很多种类,某些锁可能允许对共享资源并发访问,如ReadWriteLock(读写锁),Lock和ReadWriteLock是java 5新提供的两个根接口,并为Lock提供了ReentrantLock(可重入锁)实现类,为ReadWriteLock提供了ReentrantReadWriteLock实现类。

我们使用ReentrantLock来修改取钱的代码,添加同步的限制:

  private  final ReentrantLock look=new ReentrantLock();

  public  void  draw(String name,double drawAmount){
     //加锁
      look.lock();
     try {
        if(balance>=drawAmount){
            System.out.println(name+"取钱成功! 取出="+drawAmount);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //修改金额
            balance=balance-drawAmount;
            System.out.println("\t 余额为:"+balance);
        }else{
            System.out.println(name+"取钱失败,账号余额不足!");
        }
    }finally {
        //释放锁
        look.unlock();
    }
}

上面程序中的第一行代码定义了一个ReentrantLock对象,程序中实现draw()方法时,进入方法执行后立即请求对ReentrantLock对象进行加锁,当执行完draw()方法的取钱逻辑之后,程序使用finally块来确保锁的释放。

注意: 同步方法或同步代码块使用与竞争资源相关的,隐式的同步监视器,并且强制要求加锁和释放锁要出现在一个块结构中,而且当获取到多个锁时,它们必须以相反的顺序释放,且必须在与所有锁被释放时相同的范围内释放所有锁。

同步锁(Lock)使用的注意事项:

  1. Lock提供了同步方法和同步代码块所没有的其他功能,包括用于非块结构的tryLock()方法,以及试图获取可中断锁的lockInterruptibly方法,还有获取超时失效锁tryLock(long,TimeUnit); 方法。
  2. ReentrantLock锁具有可重入性,也是说,一个线程可以对已被加锁的ReentrantLock锁再次加锁,ReentrantLock对象会维持一个计数器来追踪lock()方法的嵌套调用,线程在每次调用lock()加锁后,必须显示调用unlock()释放锁,所以一段被锁保护的代码可以调用另一个被相同锁保护的方法。

释放线程锁的锁定
注:这里说的线程锁的锁定,只指的是同步代码块和同步方法,Lock需要显示释放锁定所以不在讨论范围之内。
任何线程在进入同步代码块,同步方法之前,必须先获得对同步代码块的锁定,那么何时会释放同步监视器的锁定呢?

程序无法显示的释放同步监视器的锁定。线程会在如下几种情况下释放对同步监视器的锁定:

当前线程的同步方法,同步代码块执行结束,当前线程即释放同步监视器。
当前线程在同步代码块,同步方法中遇到break,return终止了该代码块或该方法的继续执行,当前线程会释放同步监视器。
当前线程在同步代码块,同步方法中出现了未处理的Error或Excepation,导致了该代码块,该方法异常结束时,当前线程会释放同步锁。
当前线程执行同步代码或同步方法时,程序执行了同步监视器对象的wait方法,则当前线程暂停,并释放同步监视器。
在如下情况下,线程不会释放同步锁:

线程执行同步代码块或同步方法时,程序调用了Thread.sleep(),Thread.yield()方法来暂停当前线程的执行,当前线程不会释放同步监视器。
程序执行同步代码块时,其他线程执行了该线程的suspend()方法将该线程挂起,该线程不会释放同步监视器。

死锁
最后讨论一下这个牛逼的概念以及它是怎么产生的。死锁是个啥?当两个线程相互等待对方释放同步监视器时就会发生死锁,Java虚拟机没有检测,也没有采取措施处理死锁这种情况,所以多线程编程时应该采取措施避免死锁出现。一旦出现死锁,整个程序即不会发生任何异常,也不会给出任何提示,只是所有线程处于阻塞状态,无法继续。
下面用代码来实现一下死锁,哈哈!

package com.example.thread;

/**

public class DeadLock implements Runnable {

class A{
    public synchronized void foo(B b){

      System.out.println("当前线程名:"+Thread.currentThread().getName()+"进入了A实例的foo方法");//①

      try {
            Thread.sleep(200);
      }catch (InterruptedException e){
        e.printStackTrace();
      }

      System.out.println("当前线程名:"+Thread.currentThread().getName()+"企图调用B实例的last方法");//③
      b.last();
    }

    public synchronized void last(){
        System.out.println("进入了A实例的last方法");
    }
}

class B{
  public synchronized void bar(A a){
      System.out.println("当前线程名:"+Thread.currentThread().getName()+"进入了B实例的bar方法");//②

      try {
          Thread.sleep(200);
      }catch (InterruptedException e){
          e.printStackTrace();
      }
      System.out.println("当前线程名:"+Thread.currentThread().getName()+"企图调用A实例的last方法");//④
      a.last();
  }

  public synchronized void last(){
      System.out.println("进入了B实例的last方法");
  }
}

A a=new A();
B b=new B();

public void init(){
  Thread.currentThread().setName("主线程");
   a.foo(b);
  System.out.println("进入了主线程之后");
}

@Override
public void run() {
    Thread.currentThread().setName("子线程");
    b.bar(a);
    System.out.println("进入了子线程之后");
}

public static void main(String args[]) throws Exception {
   DeadLock dl=new DeadLock();
   new Thread(dl).start();
    dl.init();
}

}

运行上面的程序:

1当前线程名:主线程进入了A实例的foo方法
2当前线程名:子线程进入了B实例的bar方法
3当前线程名:主线程企图调用B实例的last方法
4当前线程名:子线程企图调用A实例的last方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值