1. 中断线程的方法
1.1 Thread.sleep()方法
-
Thread类的一个静态方法
-
让当前正在执行的线程处于睡眠状态
-
指定睡眠时间,时间过后,自动唤醒,唤醒后,线程进入就绪状态,准备争抢CPU
-
睡眠期间,一旦被其他线程调用了当前线程的interrupt方法中断,会抛出异常
-
如果线程抢占了一个对象锁,睡眠期间不会释放对象锁。
1.2 Thread.yield()方法
-
让步,当前线程让出CPU,与其他线程重新争抢CPU
1.3 thread.join()方法
-
插队,当前线程调用指定线程对象的join方法,将指定线程加入当前线程的执行序列
-
当指定线程执行完毕后,当前线程才会继续执行。(此时直接处于运行状态)
通过底层原码可知,join方法会不挺的检测线程的存活状态
当目标线程执行完毕后,自动销毁,不再存货
当前线程一旦发现目标线程销毁,则继续执行自己的操作。在判断时一定已经获得cpu了
-
最常用的就是通过join方法,实现确保所有的其他线程都执行完毕,当前线程再继续执行。
1.4 object.wait()方法
-
这是Object对象的方法,也是所有对象都拥有的方法
-
该方法必须用在同步方法或同步代码段中
因为线程要想执行同步方法或同步代码段,需要先获得对应的锁对象
-
在同步方法或同步代码段中,获得的是哪个对象的对象锁,才可以调用哪个对象的wait方法
表示让出当前对象的对象锁,获得当前对象锁的当前线程处于等待状态
等待再次获得该对象的对象锁,才能继续执行剩下未完成部分代码
-
一般多因为逻辑数据未达标,需要临时让出对象锁,让其他线程先执行。
-
当其他线程使用完该对象的对象锁后,需要调用该对象的
notify或notifyall
方法唤醒等因为wait方法而等待该对象锁的线程。
-
wait的重载方法
obj.wait()
会一直等待,直到被另一个线程调用obj.notify or obj.notifyall
唤醒obj.wait(time)
会等待指定的毫秒数,自动唤醒,唤醒后如果依然没有获得锁,会继续等待
-
notify和notifyAll方法
-
也需要在同步方法或同步代码段中使用,并且需要先确保获得了该对象的对象锁
obj.notify()
表示随机唤醒一个因为wait方法等待当前锁的线程obj.notifyAll()
表示唤醒所有因为wait方法而等待当前锁的线程 -
-
同步队列与等待队列
-
synchronized锁是依赖于系统级别的锁,称之为重量级锁
-
在应用过程中,会产生两个队列
-
一个称为同步队列,当多个线程因调用同步方法或同步代码段时,对同一把锁进行争抢
当一个线程抢到,其他线程进入同步等待队列
-
一个称为条件队列,当一个线程已经获得了对象锁,因为某些业务条件不支持,
不得不通过wait方法让出锁,同时重新等待这把锁
此时当前线程会被存入条件等待队列
notify方法唤醒的是条件等待队列中的线程。
唤醒之后,条件等待队列中的线程会被加入同步等待队列
-
-
当线程1234按顺序争抢锁时,线程1获得锁, 线程234进入同步队列
当线程1使用完毕,释放锁后, 同步队列中的线程会按照倒序依次被唤醒
系统级别的同步队列是一个倒序唤醒过程,不代表所有的同步队列都是这个顺序(如:lock)
-
1.5 lock的wait等待
-
lock是synchronized的代替方案
-
所以也提供了类似于synchronized配合wait相关的功能
-
注意:这里使用的不是lock对象的wait方法
-
通过lock对象可以获得一个condition对象,表示条件对象
当业务逻辑中不满足某些条件的时候,可以基于Condition条件对象进行等待
Condition condition = lock.newCondition();
-
当业务执行时,遇到等待条件,通过调用
condition.await(); // "buka".wait()
线程等待进入等待队列
-
当另一个线程使用完锁之后,通过调用
condition.signal() or singalAll()
唤醒线程将等待队列中的线程重新加入同步队列
public static void main(String[] args) throws InterruptedException {
Lock lock = new ReentrantLock();
//这就是用来表示i==5的这个等待条件
Condition condition = lock.newCondition();
Thread t1 = new Thread(()->{
lock.lock();
for(int i=1;i<=10;i++){
System.out.println(i);
Thread.sleep(1000);
if( i == 5 ){
condition.await(); // "buka".wait()
}
}
lock.unlock();
});
t1.start();
Thread.sleep(10);
Thread t2 = new Thread(()->{
lock.lock();
for(int i=11;i<=20;i++){
System.out.println(i);
}
condition.signal();
lock.unlock();
});
t2.start();
}
2. FutureTasck 和 Callable
-
是JDK1.5之后,在JUC工具包提供了一个多线程工具类
-
在多线程应用中,a线程可以通过FutureTask和Callable了解b线程是否执行完毕以及b线程执行的结果。
-
可以实现两个线程之间的通信。
-
自定义线程类,实现Callable接口,重写call方法,该方法执行的功能就是之前线程run方法的功能,无非多了一个返回值即为线程执行结果
-
Callable类型的线程,不能直接被Thread执行,需要使用FutureTask进行包装
未来可以通过FutureTask获得执行的状态及执行的结果
*
```java
public static void main(String[] args) throws InterruptedException, ExecutionException {
Callable<Integer> c = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Random r = new Random();
int sum = 0 ;
for(int i=1;i<=10;i++){
System.out.println(i);
int count = r.nextInt(3)+1 ; //[1,3]
sum += count ;
try {
Thread.sleep(count*1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
return sum ;
}
};
FutureTask ft = new FutureTask<>(c);
Thread t = new Thread(ft);
t.start();
for(int i=100;i<=105;i++){
System.out.println(i);
Thread.sleep(500);
}
//主线程想知道,此时t1线程是否执行完毕,如果执行完毕,其执行的结果是多少
//System.out.println(ft.isDone());
Object r = ft.get();
System.out.println("-------->" + r);
}
```
3. Timer定时任务
-
定时操作有两种
-
1.延迟n久执行,延迟到指定时间(执行一次)
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("今天晚上好好学习");
}
};
Date now = new Date(2024,8,20,17,00);
now.setTime( now.getTime() + 5000 );
System.out.println("----启动定时器----");
Timer t1 = new Timer();
//t1.schedule(task,now); //指定的时间开始执行任务
t1.schedule(task,5000); //指定延迟多长时间执行
2.每隔n久执行(执行多次)
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("今天晚上好好学习");
}
};
Date now = new Date(2024,8,20,17,00);
now.setTime( now.getTime() + 5000 );
System.out.println("----启动定时器----");
Timer t1 = new Timer();
//t1.schedule(task,now,2000); //指定的时间开始执行任务,一旦执行,每隔2秒重复执行
t1.schedule(task,5000,2000); //指定延迟多长时间执行,每隔2秒重复执行