目录
一、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()方法是用于控制线程执行时间的暂停。