java.util.concurrent.locks.Lock锁

本文通过一个卖票程序的例子,详细对比了synchronized关键字与Lock接口在实现线程同步时的不同之处,包括它们的使用方式、锁状态感知、锁释放机制等。

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

Lock(锁)

先看一个简单的卖票例子(无锁):

package demo1;

public class SaleTicketTest {
    public static void main(String[] args) {
        // lambda 表达式 ()表示run() 其中"()"中可以写明参数 ,->{} 表示 run后的代码块{}
        // 支持 lambda 表达式,必须是接口上有 @FunctionalInterface 注解
        // @FunctionalInterface 注解的接口,只能存在一个 抽象方法!

        Ticket ticket = new Ticket();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"C").start();

    }
}
class Ticket{
    private Integer num = 40;
    public void sale(){
        if(num > 0){
            num --;
            System.out.println(Thread.currentThread().getName()+"卖票,剩余:"+num+"张");
        }
    }
}

多运行几次,得到的日志信息以下所示:
在这里插入图片描述

顺序杂乱!

JDK8新特性:函数式接口@FunctionalInterface的使用说明

JDK 1.5之前的做法:

采取 synchronized进行加锁!

class Ticket{
    private Integer num = 40;
    public synchronized void sale(){
        if(num > 0){
            num --;
            System.out.println(Thread.currentThread().getName()+"卖票,剩余:"+num+"张");
        }
    }
}

JDK 1.5 之后的做法:
可以使用java.util.concurrent.locks.Lock类实现加锁释放锁操作。

package demo1;

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

public class SaleTicketTest3 {
    public static void main(String[] args) {
        // lambda 表达式 ()表示run() 其中"()"中可以写明参数 ,->{} 表示 run后的代码块{}
        // 支持 lambda 表达式,必须是接口上有 @FunctionalInterface 注解
        // @FunctionalInterface 注解的接口,只能存在一个 抽象方法!

        Ticket3 ticket3 = new Ticket3();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket3.sale();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket3.sale();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket3.sale();
            }
        },"C").start();

    }
}

class Ticket3{
    private Integer num = 40;//目标数
    private Integer saleNum = 0; //卖出数

    // 可重入锁(最实用)
    // 调用无参构造,生成 “不公平锁”
    // 可以传递参数 ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();} true为公平锁
    // 不公平锁,性能更好,可以插队!
    Lock lock = new ReentrantLock();
    public void sale(){
        lock.lock(); //加锁
        //lock.tryLock();
        try {
            if(num > 0){
                num --;
                saleNum ++;
                System.out.println(Thread.currentThread().getName()+"卖票,共卖了"+saleNum+"张,剩余:"+num+"张");
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();//释放锁
        }
    }
}

synchronized和Lock的区别

  • synchronized 是java关键字,Lock只是java中的一个类。
  • synchronized 无法感知锁的状态,但是Lock可以判断是否成功拿到锁。
  • synchronized 自动加锁和释放锁,但Lock必须手动。
  • 如果拿到synchronized的线程阻塞,其他线程会继续等待;但Lock可能不等待(lock.tryLock())。
  • synchronized 可重入锁、不可中断、不公平;
    Lock 可重入锁,可以判断是否中断、可以选择公平锁/非公平锁。
  • synchronized 适合于少量代码同步问题;Lock适合大量的同步代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值