一、线程的创建与运行
- 实现Runable接口的run方法,作为Thread的构造参数
- 继承Thread类,重写run方法(好处:this等价于Thread.curentThread();坏处:java只能单继承)
- 使用FutureTask方式(好处,可以获取返回结果)
样例3:
import javax.swing.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Main extends JFrame {
public static void main(String[] args) {
FutureTask<String> futureTask = new FutureTask<>(new CallerTask());
new Thread(futureTask).start();
try {
// 阻塞的
String result = futureTask.get();
System.out.println(result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
static class CallerTask implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(3000L);
return "AAA";
}
}
}
tip:start方法只是让线程处于就绪状态,并非立即启动
二、线程交互基础方法
- obj.wait():线程挂起,直到 1.其他线程调用obj.notify()或obj.notifyAll(); 2.其他线程调用thread1.interrupt();
- obj.notify:唤醒一个obj挂起的线程,但该线程必须和其他线程竞争obj锁,抢到了才能继续执行
- obj.notifyAll:唤醒所有
- thread1.join:等待线程执行完毕,阻塞
- Thread.sleep:线程会暂时让出指定时间的执行权,但监视器资源(如:锁)仍持有,时间过后线程处于就绪状态,获取CPU资源后就可以继续运行
- Thread.yield:暗示线程调度器当前线程请求让出自己的CPU使用(即告诉线程调度器我剩下的时间片不要了,你执行下一轮调度吧),但是可能被忽略
- thread1.interrupt:给thread1设置中断标志,当thread1调用wait,join,sleep而被阻塞挂起时,抛出异常,中断线程
- Thread.isInterrupted():当前线程是否中断,不清除标志
- Thread.interrupted():当前线程是否中断,清除标志
tip:
- 调用wait或是notify方法之前都需要获取该对象的监视器锁1:synchronized(obj){}; 2:synchronized void f(){wait();}; obj.f();
- 线程可能存在虚假唤醒,即没有线程调用obj.notify的情况下被唤醒
- wait方法只会释放当前对象的监视器锁,其他监视器锁仍处于持状态
三、上下文切换的概念
- CUP资源的分配采用了时间片轮转的策略,也就是给每个线程分配一个时间片,线程在时间片内占用CPU执行任务。当前线程使用完时间片后,就会处于就绪状态并让出CPU给其他线程占用,这就是上下文切换。切换时需要保存当前线程的执行现场,当再次执行时根据保存的信息恢复执行现场。
切换时机:1、时间片用完处于就绪状态; 2、线程被其他线程中断
四、死锁条件
- 互斥条件:锁资源有排他性
- 请求并持有条件:1、有锁资源》》2、请求其他锁》》3、其他锁被占有》》4、阻塞同时不释放锁资源
- 不可剥夺条件:获取到的锁资源在自己用完之前不能被其他线程占有
- 环路等待条件:A等B,B等C,C等A
避免死锁的方法:破坏四个条件之一,当前只能破坏请求并持有和环路等待
- 例如:资源申请有序性原则(只有获取了第n-1个资源才能申请获取第n个资源)
五、守护线程和用户线程
- 当最后一个非守护线程结束时,JVM会正常退出
- 设置守护线程:thread.setDaemon(true);
六、ThreadLocal与InheritableThreadLocal
- ThreadLocal:1、每个线程操作ThreadLocal共享变量时都会复制一个副本到本地线程,所以操作的都是本地线程变量;2、变量不支持父子线程的继承
- InheritableThreadLocal:支持父子线程的继承,是在new Thread()的时候复制父线程inheritableThreadLocals的一个副本到子线程,本质上还是互相独立的
例ThreadLocal:
public class Main extends JFrame {
static ThreadLocal<String> localValue = new ThreadLocal<>();
public static void main(String[] args) throws FileNotFoundException, InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
localValue.set("1");
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 为空
System.out.println(localValue.get());
}
});
thread1.start();
thread2.start();
}
}