java多线程 -- 同步鎖

为了解决多线程安全问题
在 Java 5.0 之前,协调共享对象的访问时可以使用的机制只有 synchronized 和 volatile 。
Java 5.0 后增加了一些新的机制,但并不是一种替代内置锁的方法,而是当内置锁不适用时,作为一种可选择的高级功能。

隐式锁

synchronized

  1. 同步代码块;
  2. 同步方法。

显示锁 Lock

jdk 1.5 后:同步锁 Lock 需要通过 lock() 方法上锁,必须通过 unlock() 方法进行释放锁。
ReentrantLock 实现了 Lock 接口,并提供了与synchronized 相同的互斥性和内存可见性。但相较于synchronized 提供了更高的处理锁的灵活性。

Lock使用方式:

Lock lock = new ReentrantLock();
lock.lock();
try {
} finally {
lock.unlock();
}

相关API:

  1. void lock() 获取锁。
  2. void lockInterruptibly() 如果当前线程未被中断,则获取锁。
  3. Condition newCondition() 返回绑定到此 Lock 实例的新 Condition 实例。
  4. boolean tryLock() 仅在调用时锁为空闲状态才获取该锁。
  5. boolean tryLock(long time, TimeUnit unit) 如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁。
  6. void unlock()释放锁。

不使用锁出现多线程安全问题demo:

复制代码
package com.company;

public class TestLock {

    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        new Thread(ticket, "1号窗口").start();
        new Thread(ticket, "2号窗口").start();
        new Thread(ticket, "3号窗口").start();
    }

}

class Ticket implements Runnable{

    private int tick = 100;


    @Override
    public void run() {
        while(true){
            if(tick > 0){
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                }

                System.out.println(Thread.currentThread().getName() + " 完成售票,余票为:" + --tick);
            }

        }
    }

}
复制代码

看出现的问题(截取结果中一部分):

复制代码
3号窗口 完成售票,余票为:10
1号窗口 完成售票,余票为:8
2号窗口 完成售票,余票为:8
3号窗口 完成售票,余票为:8
2号窗口 完成售票,余票为:7
1号窗口 完成售票,余票为:7
3号窗口 完成售票,余票为:7
1号窗口 完成售票,余票为:6
2号窗口 完成售票,余票为:5
3号窗口 完成售票,余票为:4
3号窗口 完成售票,余票为:3
1号窗口 完成售票,余票为:3
2号窗口 完成售票,余票为:3
3号窗口 完成售票,余票为:2
1号窗口 完成售票,余票为:0
2号窗口 完成售票,余票为:1
复制代码

现在我们用Lock来进行处理:

复制代码
package com.company;

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

public class TestLock {

    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        new Thread(ticket, "1号窗口").start();
        new Thread(ticket, "2号窗口").start();
        new Thread(ticket, "3号窗口").start();
    }

}

class Ticket implements Runnable {

    private int tick = 100;
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();
                if (tick > 0) {
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                    }

                    System.out.println(Thread.currentThread().getName() + " 完成售票,余票为:" + --tick);
                }
            } finally {
                lock.unlock();
            }


        }
    }

}
复制代码

看下结果(当然也是一部分),很完美解决多线程安全问题:

复制代码
2号窗口 完成售票,余票为:20
2号窗口 完成售票,余票为:19
2号窗口 完成售票,余票为:18
2号窗口 完成售票,余票为:17
2号窗口 完成售票,余票为:16
2号窗口 完成售票,余票为:15
2号窗口 完成售票,余票为:14
2号窗口 完成售票,余票为:13
2号窗口 完成售票,余票为:12
2号窗口 完成售票,余票为:11
2号窗口 完成售票,余票为:10
2号窗口 完成售票,余票为:9
2号窗口 完成售票,余票为:8
2号窗口 完成售票,余票为:7
2号窗口 完成售票,余票为:6
2号窗口 完成售票,余票为:5
2号窗口 完成售票,余票为:4
2号窗口 完成售票,余票为:3
2号窗口 完成售票,余票为:2
2号窗口 完成售票,余票为:1
2号窗口 完成售票,余票为:0
复制代码

 

posted @   西北野狼  阅读(247)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示