java重入锁和同步锁有什么区别
在Java中,重入锁(ReentrantLock)和同步锁(Synchronized)都是用于实现线程同步的机制,但它们有一些区别。
- 可重入性:
-
- 重入锁是可重入的,也就是说,同一个线程可以多次获取同一个重入锁而不会产生死锁。在获取锁之后,线程可以多次进入被保护的代码块,并且每次退出代码块时都要释放锁。
- 同步锁也是可重入的。当一个线程获取到同步锁后,可以再次获取同一个锁而不会产生死锁。同步锁的可重入性是由Java虚拟机自动实现的。
- 锁的获取方式:
-
- 重入锁需要手动获取和释放,即通过调用
lock()
方法获取锁,然后在合适的时候调用unlock()
方法释放锁。 - 同步锁是隐式获取和释放的,当线程进入同步代码块时,会自动获取同步锁;当线程退出同步代码块时,会自动释放同步锁。
- 重入锁需要手动获取和释放,即通过调用
- 粒度:
-
- 重入锁提供了更细粒度的控制。可以通过使用
tryLock()
方法尝试获取锁、使用lockInterruptibly()
方法支持可中断的获取锁等。 - 同步锁相对来说粒度较大,只能使用
synchronized
关键字来修饰代码块或方法。
- 重入锁提供了更细粒度的控制。可以通过使用
- 灵活性:
-
- 重入锁提供了一些高级功能,如公平性设置、条件变量等,可以更灵活地控制线程的访问顺序和条件等待。
- 同步锁相对简单,没有提供类似的高级功能。
什么场景用重入锁好
重入锁(ReentrantLock)在以下场景中通常比同步锁(Synchronized)更适用:
- 公平性要求:重入锁可以通过构造函数的参数设置为公平锁,从而按照线程请求锁的顺序来获取锁。而同步锁无法直接实现公平性,它总是采用非公平的方式获取锁。
- 可中断性要求:重入锁提供了
lockInterruptibly()
方法,可以在获取锁的过程中响应中断请求。而同步锁在获取锁的过程中无法响应中断请求,只能等待获取锁。 - 尝试获取锁:重入锁提供了
tryLock()
方法,可以尝试获取锁而不会发生阻塞。该方法可以用于实现一些特殊的业务逻辑,例如尝试获取锁一段时间后放弃。 - 多个条件变量:重入锁可以使用
newCondition()
方法创建多个条件变量,可以更灵活地实现线程之间的通信和协作。同步锁只能使用wait()
和notify()
方法进行线程的等待和唤醒。
总的来说,如果需要更高级的功能、更细粒度的控制、公平性要求或可中断性要求,重入锁是一个更好的选择。但在一些简单的同步场景中,同步锁通常更加方便和易用。
重入锁如何锁类
重入锁(ReentrantLock)提供了一个条件变量(Condition)机制,可以用于线程之间的通信和协作。下面是一个示例:
代码语言:javascript复制ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
new Thread(()->{
// 线程1
lock.lock();
try {
System.out.println("1");
// 执行一些操作
while (true) {
System.out.println("1 s");
condition.await(); // 等待条件变量
System.out.println("1 e");
}
// 执行其他操作
} catch (InterruptedException e) {
System.out.println("1 InterruptedException");
throw new RuntimeException(e);
} finally {
lock.unlock();
System.out.println("1 end");
}
}).start();
Thread.sleep(1000);
new Thread(()->{
// 线程2
lock.lock();
try {
System.out.println("2");
// 执行一些操作
condition.signal(); // 唤醒线程1
System.out.println("2 end");
} finally {
lock.unlock();
}
}).start();
Thread.currentThread().join();
在上述示例中,我们创建了一个ReentrantLock
对象lock
和一个条件变量condition
。在线程1中,我们获取锁并执行一些操作,然后在满足某个条件时,调用await()
方法等待条件变量。在线程2中,我们获取锁并执行一些操作,然后设置条件变量并调用signal()
方法唤醒线程1。
这样就可以使用条件变量来实现线程之间的通信和协作。需要注意的是,在使用条件变量时,需要先获取锁并在try-finally
块中释放锁,以确保在任何情况下都能正确释放锁。
此外,需要注意的是,在等待条件变量时,应该总是使用while
循环来检查条件是否满足,而不是使用if
语句。这是因为,在多线程环境中,可能会出现虚假唤醒的情况,即线程在没有收到信号的情况下被唤醒。使用while
循环可以避免这种情况。
同步锁如何锁类
在Java中,可以使用synchronized
关键字来锁住类。具体来说,可以在静态方法或静态代码块中使用synchronized
关键字来锁住类。下面是一个示例:
public class MyClass {
// 静态变量
private static int count = 0;
// 静态方法
public static synchronized void increment() {
count ;
}
// 静态代码块
static {
synchronized(MyClass.class) {
// 执行需要同步的代码块
// ...
}
}
}
在上述示例中,我们定义了一个类MyClass
,其中包含一个静态变量count
和一个静态方法increment()
。在increment()
方法中,我们使用synchronized
关键字来锁住该方法,以确保在同一时刻只有一个线程可以访问该方法。
另外,在静态代码块中,我们使用synchronized
关键字来锁住类对象MyClass.class
,以确保在同一时刻只有一个线程可以执行静态代码块中的代码。
这样就可以使用synchronized
关键字来锁住类,确保在同一时刻只有一个线程可以访问被保护的代码块。需要注意的是,在实际使用中,需要谨慎使用类锁,以避免死锁或性能问题等,当然也可以锁住某个对象。
public static void main(String[] args) {
Map<String, String> list = new HashMap<String, String>();
for (int i = 0; i < 5; i ) {
String str = new String("hello" i);
if (list.containsKey(str)) {
System.out.println(str "存在");
} else {
String clock = new String("123");
list.put(str, clock);
}
}
System.out.println(Json.getJsonFromObject(list));
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i ) {
String str = new String("hello" i);
executorService.execute(() -> {
String s = list.get(str);
System.out.println(s "========" str);
synchronized (s) {
System.out.println(str "获得锁");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(str "释放锁");
});
}
System.out.println();
}
在如上的代码示例中,你可以看到使用同步锁来锁住某个固定的对象,以达到细粒度锁的功能。
最后
总体来说,重入锁相对于同步锁提供了更多的灵活性和控制能力,但使用起来也更加复杂。在大部分情况下,使用同步锁已经足够满足线程同步的需求。只有在需要更高级的功能或更细粒度控制时,才需要考虑使用重入锁。
点赞关注评论一键三连,每周分享技术干货、开源项目、实战经验、国外优质文章翻译等,您的关注将是我的更新动力!