Java多线程安全与锁机制入门(三)

一、什么是线程安全问题?

当多个线程同时操作​​同一个数据​​时,可能出现数据混乱的情况。就像多个收银员同时操作同一台收银机,可能出现金额错乱。

二、如何保证线程安全?
方法1:同步代码块
private Object lock = new Object(); // 随便创建一个对象当锁

public void sellTicket() {
    synchronized(lock) { // 给代码上锁
        if(tickets > 0) {
            System.out.println("卖出第" + tickets-- + "张票");
        }
    }
}

注意:锁对象​​必须唯一(建议用final修饰)

就像给厕所门上锁,同一时间只允许一个人使用

方法2:同步方法
public synchronized void sellTicket() { 

    // 自动使用当前对象(this)当锁
    if(tickets > 0) {
        System.out.println("卖出第" + tickets-- + "张票");
    }
}

 注意:普通方法用this当锁

静态方法用类对象当锁(如MyClass.class

Thread中同步方法只能用静态方法
方法3:Lock锁(更灵活)
Lock lock = new ReentrantLock(); // 创建锁对象

public void sellTicket() {
    lock.lock(); // 手动上锁
    try {
        if(tickets > 0) {
            System.out.println("卖出第" + tickets-- + "张票");
        }
    } finally {
        lock.unlock(); // 必须解锁!
    }
}

注意:优点:可中断、可设置超时时间

必须写在finally中保证解锁

 三、两种锁对比

synchronized jvm级别的锁,jvm自动上锁和解锁

Lockjava代码写的锁,需要手动的加锁和释放锁

四、对象锁与类锁

使用 synchronized 关键字来实现线程同步

import java.util.concurrent.TimeUnit;


public class TestThread8 {
    public static void main(String[] args) {

        // 创建 TestThread8 类的一个实例对象
        TestThread8 tt1 = new TestThread8();
        
        // TestThread8 tt2 = new TestThread8();

        new Thread(() -> {
            tt1.t1();
        }).start();

        new Thread(() -> {
            tt1.t2();
        }).start();
    }

    // 使用 synchronized 块对 TestThread8 类的 Class 对象加锁
    public void t1() {
        // 这意味着同一时间只有一个线程可以进入该块执行代码
        synchronized (TestThread8.class) {
            System.out.println("进入 t1 方法");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("结束 t1 方法");
        }
    }

    // 使用 synchronized 关键字修饰
    // 静态方法的同步锁是该类的 Class 对象,即 TestThread8.class
    public static synchronized void t2() {
        System.out.println("进入 t2 方法");
        try {
            // 线程休眠 3 秒,模拟耗时操作
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("结束 t2 方法");
    }
}

 五、常见问题解决指南
出现死锁怎么办?

​死锁场景​​:

  1. 线程A拿着锁1等锁2
  2. 线程B拿着锁2等锁1

​解决方法​​:

  1. 按固定顺序获取锁
  2. 使用tryLock()设置等待时间
    if(lock.tryLock(500, TimeUnit.MILLISECONDS)) { // 最多等500ms
        // 拿到锁后的操作
    }

六、最佳实践总结 
  1. 优先使用​​:java.util.concurrent包中的线程安全类

    • ConcurrentHashMap
    • CopyOnWriteArrayList
  2. ​锁使用原则​​:

    • 锁的范围尽量小
    • 锁对象用final修饰
    • 避免在锁内调用其他方法
  3. ​简单场景​​用synchronized,​​复杂需求​​用Lock锁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值