Java中Lock锁详解

 

目录

 一、Lock锁的基本使用

 二、Condition类详解 

 三、进程的优先级

四、wait/join与sleep的区别:


 一、Lock锁的基本使用

在Java中,Lock是一个接口,它提供了比synchronized关键字更高级的线程同步机制。使用Lock接口可以创建更复杂和灵活的同步结构。

Lock接口的常用实现类有ReentrantLock和ReentrantReadWriteLock,它们提供了可重入的互斥锁和读写锁。

使用Lock锁的一般步骤如下:

1. 创建一个`Lock`对象实例。

Lock lock = new ReentrantLock();
```

2. 在需要进行同步的代码块中,通过调用`lock()`方法来获取锁。

lock.lock();
try {
    // 同步的代码
} finally {
    // 在finally块中释放锁,以确保锁的释放
    lock.unlock();
}
```

3. 在同步的代码块执行完之后,通过调用`unlock()`方法释放锁。示例代码:

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

public class Main {
    private static Lock lock = new ReentrantLock();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            acquireLock();
        });

        Thread t2 = new Thread(() -> {
            acquireLock();
        });

        t1.start();
        t2.start();
    }

    private static void acquireLock() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 获取到了锁");
            // 执行同步的代码块
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + " 释放了锁");
            lock.unlock(); // 释放锁
        }
    }
}
```

输出:
```
Thread-0 获取到了锁
Thread-0 释放了锁
Thread-1 获取到了锁
Thread-1 释放了锁
```

在上述示例中,我们创建了一个ReentrantLock实例作为锁对象。然后,我们创建两个线程t1和t2,它们都会调用acquireLock()方法获取锁并执行同步的代码块。最后,我们可以看到两个线程交替地获取到锁、执行同步代码块并释放锁。

需要注意的是,在使用Lock接口时要注意在finally块中释放锁,以确保在任何情况下都能正常释放锁。否则可能会导致线程出现死锁的情况。

使用Lock锁可以灵活控制线程的同步和互斥,并提供了更多的高级功能,例如可中断的锁、条件变量等,可以更好地实现复杂的并发控制需求。

 二、Condition类详解 

在Java中,Condition类是Java.util.concurrent包下的一个接口,用于支持线程的等待和通知机制。它通常与Lock接口一起使用,用于实现线程间的同步和协调。

Condition类提供了以下方法:

1. await():使当前线程等待,直到被其他线程调用signal()或signalAll()方法唤醒。

2. awaitUninterruptibly():类似于await()方法,但是在等待期间不会响应线程中断。

3. await(long time, TimeUnit unit):使当前线程等待一段时间,在指定的时间内没有被其他线程调用signal()或signalAll()方法唤醒,将自动唤醒。

4. awaitNanos(long nanosTimeout):使当前线程等待一段纳秒时间,在指定的时间内没有被其他线程调用signal()或signalAll()方法唤醒,将自动唤醒。

5. awaitUntil(Date deadline):使当前线程等待直到某个时间,如果在指定时间内没有被其他线程调用signal()或signalAll()方法唤醒,将自动唤醒。

6. signal():唤醒一个等待在Condition上的线程,并使其从await()方法返回。

7. signalAll():唤醒所有等待在Condition上的线程,并使它们从await()方法返回。使用示例:

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

public class ConditionExample {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public void doSomething() {
        lock.lock();
        try {
            // 等待条件
            condition.await();
            // 执行其他操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void notifyThread() {
        lock.lock();
        try {
            // 唤醒线程
            condition.signal();
        } finally {
            lock.unlock();
        }
    }
}
```

在上述示例中,使用lock对象创建了一个Condition实例condition。在doSomething()方法中,线程将调用condition.await()进入等待状态,直到其他线程调用condition.signal()方法唤醒它。notifyThread()方法用于唤醒等待在condition上的线程。

这样,通过Condition类的使用,我们可以实现线程之间的等待和通知机制,实现更灵活的线程同步与协调。

 三、进程的优先级

在Java中,可以使用Thread类的setPriority(int priority)方法来设置线程的优先级。Java中的线程优先级范围是1(最低优先级)到10(最高优先级),其中5为默认优先级。

一个线程的优先级仅仅是给调度器一个提示,告诉它该如何调度线程,实际上是由操作系统来决定如何处理线程的优先级。不同操作系统可能会有不同的处理方式,且并不能保证线程优先级会完全按照设定的顺序高低执行。

以下是设置线程优先级的示例代码:

package lzx6;

public class ThreadPriorityExample {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            for (int i = 0;; i++) {
                System.out.println("Thread 1: " + i);
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0;;i++ ) {
                System.out.println("Thread 2: " + i);
            }
        });

        // 设置线程的优先级
        thread1.setPriority(Thread.MIN_PRIORITY);  // 最低优先级
        thread2.setPriority(Thread.MAX_PRIORITY);  // 最高优先级

        // 启动线程
        thread1.start();
        thread2.start();
    }
}

运行结果:

可以看到确实进程2的优先级更高。

在上述代码中,我们创建了两个线程thread1和thread2,分别输出各自线程标识和循环变量的值。我们通过调用thread1.setPriority(Thread.MIN_PRIORITY)设置thread1线程为最低优先级,通过调用thread2.setPriority(Thread.MAX_PRIORITY)设置`thread2`线程为最高优先级。

需要注意的是,线程的优先级只是给调度器提供了一个提示,不同的操作系统可能有不同的实现方式,而且不能保证线程会按照设定的优先级顺序执行。此外,线程优先级的相对差异通常只在较忙碌的系统中才能明显体现出来,在负载较轻的系统中可能不会有明显的效果。Oracle提供的Linux的Java虚拟机中,线程优先级都是一样的。

四、wait/join与sleep的区别:

wait(), join(), 和 sleep() 是用于控制线程执行的方法,它们在用途和行为上有一些区别。

1. wait(): 是Object类的方法,用于使当前线程进入等待状态,直到其他线程调用相同对象上的notify()或notifyAll()方法来唤醒等待的线程。wait()方法必须在同步代码块或同步方法中调用。

2. join(): 是Thread类的方法,用于等待调用join()方法的线程执行完毕。当一个线程调用其他线程的join()方法时,当前线程会进入等待状态,直到被调用的线程执行完毕。join()方法通常用于多线程协作,以确保线程执行的顺序。

3. sleep(): 是Thread类的方法,用于使当前线程进入休眠状态(阻塞),暂时中止执行一段时间。sleep()方法会让线程暂停执行指定的时间,然后重新进入可运行状态。

关键区别如下:

- wait()和join()方法都依赖于其他线程的操作,而`sleep()方法是通过指定的时间来控制线程执行。
- wait()方法在等待期间会释放对象的锁,而sleep()方法和join()方法在等待期间仍然持有对象的锁。
- wait()方法必须在同步代码块或同步方法中调用,而sleep()方法和join()方法可以在任何地方调用。
- wait()方法需要被唤醒后才能继续执行,而sleep()方法和join()方法可以在指定的时间过去后自动继续执行。

总结来说,wait()方法和join()方法是用于线程间的通信和协作,sleep()方法是用于控制线程执行时间的暂停。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jay/.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值