synchronized和lock

文章介绍了Java中synchronized关键字和Lock接口(以ReentrantLock为例)的使用,包括同步方法、同步代码块、Lock的lock/unlock方法以及Condition的使用。对比了synchronized和Lock在获取方式、使用方式、灵活性、性能和可中断性上的差异,并通过示例展示了Condition如何实现线程间的通信。

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

synchronized和lock

synchronized

synchronized关键字用于Java中同步方法或同步代码块中,防止资源冲突。当任务要执行被synchronized关键字保护的代码片段的时候,它将检查锁是否可用,然后获取锁,执行代码,释放锁。

基本使用

同步方法
public synchronized void synchronizedMethod() {
    // 该方法的内容在同一时间只能被一个线程执行
    // ...
}
同步代码块
public void someMethod() {
    // 非同步的代码

    synchronized (this) {
        // 被同步的代码块
        // ...
    }

    // 非同步的代码
}

其余不在赘述,相关使用应该很常见

lock

ReentrantLock

除了使用synchronized外,我们可以使用Lock接口写的ReentrantLock 实现独占锁的功能。

查看类图可知,ReentrantLock 实现了Lock接口

Lock接口定义了几个方法用来实现锁的基本功能

  • lock() : void //获得锁。
  • lockInterruptibly() : void // 获取锁定,除非当前线程是 interrupted
  • tryLock() : boolean // 只有在调用时才可以获得锁
  • tryLock(time : long,unit : TimeUnit ) : boolean // 如果在给定的等待时间内是空闲的,并且当前的线程尚未得到 interrupted,则获取该锁。
  • unlock() : void // 释放锁。
  • newCondition() : Condition // 返回一个新Condition绑定到该实例Lock实例。

常用方法应该是lock()/unlock()和newCondition(),简要阐述一下,由ReentrantLock实现

lock和unlock

lock和unlock方法是Lock接口提供的重要的加锁解锁方法,他们包围的代码片段就是相应的加锁处理片段,下面是一个简单的示例

public class LockExample {
    
    private Lock lock   = new ReentrantLock();
    
    private Object object = new Object();

    public  void test(String[] args) {
        lock.lock();
        System.out.println("lock 测试"+ object.toString());
        lock.unlock();
    }
}

只需要新建Lock对象后在需要锁的开始和结束处使用lock方法和unlock方法进行加锁和解锁就可以完成相应的功能。

需要注意,如果锁内部代码出现异常可能会造成lock对象的unlock方法没有执行,从而未释放锁。所以相应的功能应该卸载try catch代码块中

public class LockExample {

    private Lock lock   = new ReentrantLock();

    private Object object = new Object();

    public  void test(String[] args) {
        
        lock.lock();
        try{
            System.out.println("lock 测试"+ object.toString());

        }catch (Exception e){
            e.printStackTrace();
        }
        finally {
            lock.unlock();
        }
    }
}
newCondition

newCondition()方法是Lock接口中线程通讯的重要实现,他返回一个Condition对象,Condition类似于Object监视器方法( waitnotifynotifyAll ),将其中的重要方法封装成了不同的对象,以得到具有多个等待集的每个对象,通过将它们与使用任意的组合的效果Lock实现。 Lock替换synchronized方法和语句的使用, Condition取代了对象监视器方法的使用。 下面是一个简单的示例

package com.juc;


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

public class Counter {
    // 定义一个共享的计数器
    private int count = 0;

    // 定义一个锁和条件变量
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    // 定义一个增加计数器的方法
    public void increment() {
        lock.lock();
        try {
            count++;
            System.out.println("Counter value: " + count);
            // 通知其他线程,计数器已经增加
            condition.signal();
        } finally {
            lock.unlock();
        }
    }

    // 定义一个减少计数器的方法
    public void decrement() {
        lock.lock();
        try {
            while (count == 0) {
                // 等待其他线程通知,计数器已经增加
                condition.await();
            }
            count--;
            System.out.println("Counter value: " + count);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        Counter counter = new Counter();

        // 创建两个线程,一个增加计数器,一个减少计数器
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                counter.increment();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                counter.decrement();
            }
        });

        // 启动线程
        t1.start();
        t2.start();

        // 等待线程结束
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

该类定义了一个资源类和两个方法,一个增加计数,一个减少计数,循环5次,后通知另一线程执行,结果如下:

Counter value: 1
Counter value: 2
Counter value: 3
Counter value: 4
Counter value: 5
Counter value: 4
Counter value: 3
Counter value: 2
Counter value: 1
Counter value: 0

condition的使用类似于Object方法中的wait和notify方法,不同的是Condition类可以提供多个条件,实现更细粒度的线程等待和通知

package com.juc;

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

public class MultiConditionExample {
    private final Lock lock = new ReentrantLock();
    private final Condition condition1 = lock.newCondition();
    private final Condition condition2 = lock.newCondition();
    private boolean condition1Met = false;
    private boolean condition2Met = false;

    public void awaitCondition1() throws InterruptedException {
        lock.lock();
        try {
            while (!condition1Met) {
                condition1.await();
            }
            // condition1Met 为 true,执行相关操作
            System.out.println("Condition 1 开始运行");
        } finally {
            lock.unlock();
        }
    }

    public void awaitCondition2() throws InterruptedException {
        lock.lock();
        try {
            while (!condition2Met) {
                condition2.await();
            }
            // condition2Met 为 true,执行相关操作
            System.out.println("Condition 2 met");
        } finally {
            lock.unlock();
        }
    }

    public void signalCondition1() {
        lock.lock();
        try {
            condition1Met = true;
            condition1.signal();
        } finally {
            lock.unlock();
        }
    }

    public void signalCondition2() {
        lock.lock();
        try {
            condition2Met = true;
            condition2.signal();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        MultiConditionExample example = new MultiConditionExample();

        Thread thread1 = new Thread(() -> {
            try {
                Thread.sleep(2000);
                example.signalCondition1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread thread2 = new Thread(() -> {
            try {
                Thread.sleep(3000);
                example.signalCondition2();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread thread3 = new Thread(() -> {
            try {
                example.awaitCondition1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread thread4 = new Thread(() -> {
            try {
                example.awaitCondition2();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();

        try {
            thread1.join();
            thread2.join();
            thread3.join();
            thread4.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结果

Condition 1 met
Condition 2 met

这个示例只是将一对Condition信号复制了一份放在一个类中执行,不同于wait/notify,Condition在这个可以根据实例信号(condition1/condition2)来指定需要通讯的线程。

Synchronized和Lock对比

  1. 获取方式
    • Synchronized:通过 synchronized 关键字来实现,可以修饰方法或代码块。
    • Lock:通过 Lock 接口的实现类(如 ReentrantLock)来创建实例,并调用其方法进行获取锁。
  2. 使用方式
    • Synchronized:在方法上使用 synchronized 关键字或使用 synchronized 块来指定需要同步的代码块。
    • Lock:使用 Lock 对象的 lock() 方法获取锁,然后使用 unlock() 方法释放锁。
  3. 灵活性
    • Synchronized:是内置的、隐式的锁机制,具有自动释放锁的特性,可以很方便地使用,但相对较为简单,提供的功能相对有限。
    • Lock:是显式的锁机制,提供了更多的灵活性和功能,例如可重入性、公平性、条件变量等,但需要手动管理锁的获取和释放。
  4. 性能
    • Synchronized:在 Java 6 之后进行了很多优化,性能已经大幅提升,尤其在无竞争的情况下,性能表现良好。
    • Lock:性能相对较好,并且在高度竞争的情况下能够提供更好的性能表现。
  5. 可中断性
    • Synchronized:一旦获取到锁,无法被其他线程中断,只能等待锁的释放。
    • Lock:提供了可中断的获取锁的方法 lockInterruptibly(),可以在等待锁的过程中响应中断。
  6. 条件变量
    • Synchronized:不直接支持条件变量,但可以通过 Objectwait()notify()notifyAll() 方法结合 synchronized 关键字来实现类似的功能。
    • Lock:提供了 Condition 接口来支持条件变量,可以使用 newCondition() 方法创建条件对象,并使用其 await()signal()signalAll() 方法进行线程等待和通知。
参考:

JDK官方文档:Synchronized Methods (The Java™ Tutorials > Essential Java Classes > Concurrency) (oracle.com)

synchronized VS Lock, wait-notify VS Condition

chatGPT3.5

### synchronized Lock 的区别及使用场景 #### 1. 基本概念 `synchronized` 是 Java 中用于实现线程同步的关键字,它在 JVM 层面实现锁机制。`Lock` 是一个接口,提供了比 `synchronized` 更加灵活的锁机制[^4]。 #### 2. 功能对比 - **锁的获取与释放** `synchronized` 在方法或代码块执行完毕后自动释放锁,而 `Lock` 需要显式调用 `lock()` 方法获取锁,并在 `finally` 块中调用 `unlock()` 方法释放锁。 - **可中断性** `synchronized` 不支持锁的中断,而 `Lock` 提供了 `tryLock()` `lockInterruptibly()` 等方法,允许线程在等待锁时被中断[^4]。 - **超时功能** `synchronized` 不支持超时机制,而 `Lock` 提供了 `tryLock(long timeout, TimeUnit unit)` 方法,允许线程在指定时间内尝试获取锁。 - **性能表现** 在简单的同步场景下,`synchronized` 的性能通常优于 `Lock`,因为其由 JVM 优化。然而,在需要复杂锁控制的场景下,`Lock` 的灵活性使其成为更好的选择[^1]。 #### 3. 使用示例 ##### (1) `synchronized` 示例 ```java public class Counter { private int count; public synchronized void increment() { count++; } public synchronized int getCount() { return count; } } ``` ##### (2) `Lock` 示例 ```java import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Counter { private int count; private final Lock lock = new ReentrantLock(); public void increment() { lock.lock(); // 显式获取锁 try { count++; } finally { lock.unlock(); // 显式释放锁 } } public int getCount() { lock.lock(); try { return count; } finally { lock.unlock(); } } } ``` #### 4. 使用场景 - **简单同步需求** 如果只需要简单的线程同步,且不需要复杂的锁控制(如中断、超时等),可以优先使用 `synchronized`,因为它更简洁且性能较好[^1]。 - **复杂同步需求** 如果需要更高级的锁控制(如公平锁、读写锁、锁中断等),则应选择 `Lock` 接口[^4]。 - **性能敏感场景** 在高并发场景下,如果需要对锁进行优化(如减少锁竞争),可以考虑使用 `Lock` 提供的多种锁实现(如 `ReentrantLock` 或 `ReadWriteLock`)[^1]。 #### 5. 注意事项 - 使用 `Lock` 时必须确保在 `finally` 块中释放锁,否则可能导致死锁。 - `synchronized` 是不可重入的锁,而 `ReentrantLock` 支持重入,即同一个线程可以多次获取同一把锁[^3]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值