• JavaSE——多线程中lock和synchronized的区别


    在多线程中,为了使线程安全,我们经常会使用synchronized和Lock进行代码同步和加锁

    synchronized关键字

    synchronized是Java中的关键字,是一种同步锁。synchronized可以保证方法或代码块在运行时,同一时刻只有一个线程可以进入到临界区(互斥性),同时它还保证了共享变量的内存可见性。

    synchronized使用方式有三种
    1、同步块

    //key必须是一个对象
    synchronized(key) {
    			// 同步代码
    }
    
    • 1
    • 2
    • 3
    • 4

    2、普通方法上面加

    public synchronized void method() {
            // 同步代码
    }
    
    • 1
    • 2
    • 3

    如果某个方法都是有可能出现线程安全问题,则建议加载方法上面,该方法在充当锁的钥匙!!!

    3、静态方法上面

    public synchronized static void method(){
            // 同步代码
    }
    
    • 1
    • 2
    • 3

    Java中的每个对象都可以作为锁:

    • 普通同步方法,锁是当前实例对象。
    • 静态同步方法,锁是当前类的class对象。
    • 同步代码块,锁是括号中的对象。

    没有加同步锁时

    public class TestThread04 implements Runnable{
        private int count;
        
        @Override
        public void run() {
            for (int i = 0; i < 10000; i++) {
                count++;
            }
            System.out.println(Thread.currentThread().getName() + "count = " + count);
        }
    
        public static void main(String[] args) {
            TestThread04 task = new TestThread04();
            new Thread(task).start();
            new Thread(task).start();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述
    加上同步锁后

    public class TestThread04 implements Runnable{
    
        private int count;
    //    Object obj = new Object();
    
        @Override
        public synchronized void run() {
            for (int i = 0; i < 10000; i++) {
                // ++ -- 不具备原子性
    //            synchronized (obj){
    //                count++;      Thread-0count = 19401 ,Thread-1count = 20000
    //            }
    //            count++;
            }
            System.out.println(Thread.currentThread().getName() + "count = " + count);
        }
    
        public static void main(String[] args) {
            TestThread04 task = new TestThread04();
            new Thread(task).start();
            new Thread(task).start();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    Lock接口和ReentrantLock实现类

    synchronized同步锁在jdk7之前,默认调用系统锁(重量级锁)
    基于如上原因,jdk5,JUC包诞生,提供了一种全新的锁——Lock锁

    Lock锁:
    lock锁,是jdk5.0提供的锁、是一个可重入锁(ReentrantLock)
    可以充当公平锁、也可以是不公平锁
    一旦加锁,最后必须释放该锁,否则会出现死锁现象。
    将释放锁的代码一定要放在finally中!!!

    Lock接口

    public interface Lock {
     
        // 加锁
        void lock();
        // 能够响应中断
        void lockInterruptibly() throws InterruptedException;
        // 非阻塞获取锁
        boolean tryLock();
        // 非阻塞超时获取锁
        boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
        // 解锁
        void unlock();
        // 定义阻塞条件
        Condition newCondition();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    lock():用来获取锁,如果锁被其他线程获得则进行等待,需要和unlock方法配合主动释放锁。发生异常时,不会主动释放锁,所以释放锁的操作放在finally块中

    lockInterruptibly():当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就使说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程

    tryLock():用来尝试获取锁,如果获取成功,则返回true。如果获取失败则返回false。也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待

    tryLock(long time, TimeUnit unit):和tryLock()类似。只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果一开始拿到锁或者在等待期间内拿到了锁,则返回true

    unlock():解锁,也就是释放锁

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class TestLock extends Thread {
    
        private int count;
        private Lock lock = new ReentrantLock();
    
        @Override
        public void run() {
            for (int i = 0; i < 10000; i++) {
                try {
                    // 加锁
                    lock.lock();
                    count++;
                } finally {
                    // 一定要记得释放锁!!
                    // 建议将释放锁的代码一定要放在finally中!!!
                    lock.unlock();
                }
            }
            System.out.println(Thread.currentThread().getName()+ ":count = "+ count);
        }
    
        public static void main(String[] args) {
            TestLock task = new TestLock();
            new Thread(task).start();
            new Thread(task).start();
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    在这里插入图片描述

    区别

    1、synchronized是java关键字,而Lock是java中的一个接口
    
    2、synchronized会自动释放锁,而Lock必须手动释放锁
    
    3、synchronized是不可中断的,Lock可以中断也可以不中断
    
    4、通过Lock可以知道线程有没有拿到锁,而synchronized不能
    
    5、synchronized能锁住方法和代码块,而Lock只能锁住代码块
    
    6、Lock可以使用读锁提高多线程读效率
    
    7、synchronized是非公平锁,ReentranLock可以控制是否公平锁
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 相关阅读:
    【Unity ShaderGraph】| 快速制作一个实用的 模型溶解效果
    本地Navicat连接内网数据库
    2023年中国轮胎模具需求量、竞争格局及行业市场规模分析[图]
    ElasticSearch学习
    智能汽车进入HPC时代,这家本土芯片厂商如何领跑市场
    《Thinking In Java》作者:不要使用并发!
    PID控制算法介绍及使用举例
    【系统架构师】-案例篇(三)NoSQL与分布式对象调用
    Spring Authorization Server 1.1 扩展实现 OAuth2 密码模式
    UML--类图的表示
  • 原文地址:https://blog.csdn.net/Demon_and_Angle_/article/details/126386768