1. LOCK 锁
它提供了与synchronized关键字类似的同步功
能,只是在使用时需要显式地获取和释放锁。虽然它缺少了(通过synchronized块或者方法所提
供的)隐式获取释放锁的便捷性,但是却拥有了锁获取与释放的可操作性、可中断的获取锁以
及超时获取锁等多种synchronized关键字所不具备的同步特性。
vod lock() 获取锁,调用当前方法后,当前线程将会获取锁,获取锁后,返回
void unlock() 释放锁
void lockInterruptibly() 可以在锁获取的同时,中断该线程
boolean tryLock()尝试非阻塞获取锁,获取到返回True,获取不到返回Flase
boolean tryLock(long time ,TimeUnit unit ) 获取锁的同时,当前线程在以下3种情况下返回
1.当前线程在超时时间内获得了锁
2.当前线程在超时时间内被中断
3.超时时间结束,返回false
Condition newCondition() 获取等到通知组件,该组件和当前锁绑定,
只有当前线程获得了锁,才能调用该
组件得wait()方法,而调用后,当前线程立即释放锁
2. 线程状态
1. 建之后,调用start()方法开始运行。当线程执行wait()方法之后,线程进入等待状态。
2. 待状态的线程需要依靠其他线程的通知才能够返回到运行状态,
3. 等待状态相当于在等待状态的基础上增加了超时限制,也就是超时时间到达时将会返回到运行状态。
4. 用同步方法时,在没有获取到锁的情况下,线程将会进入到阻塞状态。
5. 执行Runnable的run()方法之后将会进入到终止状态。
阻塞状态:
是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态,但是阻塞在
java.concurrent包中Lock接口的线程状态却是等待状态,因为java.concurrent包中Lock接口对于
阻塞的实现均使用了LockSupport类中的相关方法。
2.1 睡眠工具类
//在说线程状态之前先写一个工具类,线程睡眠的工具类,下面有用
public class SleepUtils {
public static final void second(long seconds) {
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
}
}
}
2.2 线程中断
2.2.1 暂停恢复和停止线程最原始的方式suspend()、resume()和stop()。
public class Deprecated {
/*
线程的打断和中断,原始的方法,但是这几种方法不建议用
*/
public static void main(String[] args) throws Exception {
DateFormat format = new SimpleDateFormat("HH:mm:ss");
Thread printThread = new Thread(new Runner(), "PrintThread");
printThread.setDaemon(true);
printThread.start();
TimeUnit.SECONDS.sleep(3);
// 将PrintThread进行暂停,输出内容工作停止
printThread.suspend();
System.out.println("main suspend PrintThread(暂停) at " + format.format(new Date()));
TimeUnit.SECONDS.sleep(3);
// 将PrintThread进行恢复,输出内容继续
printThread.resume();
System.out.println("main resume(恢复) PrintThread at " + format.format(new Date()));
TimeUnit.SECONDS.sleep(3);
// 将PrintThread进行终止,输出内容停止
printThread.stop();
System.out.println("main stop (停止)PrintThread at " + format.format(new Date()));
TimeUnit.SECONDS.sleep(3);
}
static class Runner implements Runnable {
@Override
public void run() {
DateFormat format = new SimpleDateFormat("HH:mm:ss");
while (true) {
System.out.println(Thread.currentThread().getName() + " Run at " +
format.format(new Date()));
SleepUtils.second(1);
}
}
}
}
2.2.2 其它线程直接对某个线程用名字进行中断
TimeUnit.SECONDS.sleep(1);
XXX.interrupt();
2.2.3 理解中断
中断可以理解为线程的一个标识位属性,它表示一个运行中的线程是否被其他线程进行
了中断操作。中断好比其他线程对该线程打了个招呼,其他线程通过调用该线程的interrupt()
方法对其进行中断操作。
线程通过检查自身是否被中断来进行响应,线程通过方法isInterrupted()来进行判断是否
被中断,也可以调用静态方法Thread.interrupted()对当前线程的中断标识位进行复位。如果该
线程已经处于终结状态,即使该线程被中断过,在调用该线程对象的isInterrupted()时依旧会返
回false。
package duoxiancheng.thread;
import java.util.concurrent.TimeUnit;
public class Interrupted {
public static void main(String[] args) throws Exception {
// sleepThread不停的尝试睡眠
Thread sleepThread = new Thread(new SleepRunner(), "SleepThread");
sleepThread.setDaemon(true);
// busyThread不停的运行
Thread busyThread = new Thread(new BusyRunner(), "BusyThread");
busyThread.setDaemon(true);
sleepThread.start();
busyThread.start();
// 休眠5秒,让sleepThread和busyThread充分运行
TimeUnit.SECONDS.sleep(5);
sleepThread.interrupt();
busyThread.interrupt();
System.out.println("SleepThread interrupted is " + sleepThread.isInterrupted());
System.out.println("BusyThread interrupted is " + busyThread.isInterrupted());
// 防止sleepThread和busyThread立刻退出
SleepUtils.second(2);
}
static class SleepRunner implements Runnable {
@Override
public void run() {
while (true) {
SleepUtils.second(10);
}
}
}
static class BusyRunner implements Runnable {
@Override
public void run() {
while (true) {
}
}
}
}
2.3 安全的终止线程
2.2.3中提到的中断状态是线程的一个标识位,而中断操作是一种简便的线程间交互
方式,而这种交互方式最适合用来取消或停止任务。除了中断以外,还可以利用一个boolean变
量来控制是否需要停止任务并终止该线程。
package duoxiancheng.thread;
import java.util.concurrent.TimeUnit;
public class Shutdown {
/*
推荐用的线程中断方式
*/
public static void main(String[] args) throws Exception {
Runner one = new Runner();
Thread countThread = new Thread(one, "CountThread");
countThread.start();
// 睡眠1秒,main线程对CountThread进行中断,使CountThread能够感知中断而结束
TimeUnit.SECONDS.sleep(1);
countThread.interrupt();
Runner two = new Runner();
countThread = new Thread(two, "CountThread");
countThread.start();
// 睡眠1秒,main线程对Runner two进行取消,使CountThread能够感知on为false而结束
TimeUnit.SECONDS.sleep(1);
two.cancel();
}
private static class Runner implements Runnable {
private long i;
private volatile boolean on = true;
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
while (on && !Thread.currentThread().isInterrupted()){
i++;
}
System.out.println("中断前Count i自增到了 = " + i);
}
public void cancel() {
on = false;
}
}
}
这种通过标识位或者中断操作的方式能够使线程在终止时有机会去清理资源,而不是武断地
将线程停止,因此这种终止线程的做法显得更加安全和优雅。
2.3 线程之间通信
notify() notifyAll()使在wait()方法里面的线程返回,返回的前题是拿到了对象的锁
wait() 使线程进入waiting状态,只能等待另外的线程通知或者被中断才能返回
而且调用wait()之后,会释放对象的锁
wait(long)等待一段时间,单位毫秒,即使没有通知也会超时返回
wait(long,int )只是精确度高了,单位到纳秒
2.4 线程等待通知的经典范式
..等待方A:
synchronized(对象) { //1. 获取对象的锁
while(条件不满足) { //2.如果条件不满足,调用对象的wait()方法
对象.wait(); 被通知后仍要检查条件
}
doSomething();对应的处理逻辑 //3. 条件满足执行对应的逻辑
}
// 通知方B:
synchronized(对象) { //1.获取对象的锁
改变条件 //2. 改变条件
对象.notifyAll(); //3. 通知所有等待在该对象上面的线程
}