创建线程(三种方法)
| 多继承 | 有无返回值 | 实现方式 | 创建方法 | 启动线程 | |
|---|---|---|---|---|---|
| 实现Runnable接口 | Y | 无 | 重写run方法 | RunableTask task = new RunableTask() | new Thread(task).start(); |
| 继承Tread类 | N | 无 | 重写run方法 | MyThread thread=new MyThread(); | thread.start(); |
| 实现Callnable | Y | 有 | 重写call方法 | FutureTask |
new Thread(Futuretask).start(); |
线程通知和等待(wait、notify、notifyAll)
wait()
线程被阻塞挂起
1、其他线程调用了该共享对象的的notify()或notifyAll()方法。
2、其他线程调用了该线程的interrupt()方法,该线程抛出interruptedException异常返回。
事先要获取监视器锁,否则会抛出IllegalMonitorStateException。
wait(int timeout)
一个线程被挂起,没有在指定时间被另一个线程调用该共享变量的notify()或notigyAll(),该函数还是会因为超时而返回。
notify()
随机唤醒一个wait()的线程
线程唤醒后要跟其他线程竞争监视器锁
事先要获取监视器锁,否则会抛出IllegalMonitorStateException。
notifyAll()
等待线程执行终止(join)
join方法是Thread类直接提供的,无参且返回值为void
线程A调用线程B的join方法后会被阻塞,当其他线程调用了线程A的interrupt()方法中断了线程A时,线程A会抛出InterruptedException异常而返回
线程睡眠(sleep)
Thread类里面的静态方法
当一个执行中的线程调用了Thread的sleep方法后,调用线程会暂时让出指定时间的执行权,也就是在这期间不参与CPU的调度,但是该线程所拥有的监视器资源,比如锁还是持有不让出的。指定的睡眠时间到了后该函数会正常返回,线程就处于就绪状态,然后参与CPU的调度,获取到CPU资源后就可以继续运行了。
当一个线程处于睡眠状态时,如果另外一个线程中断了它,会在调用sleep方法处抛出异常。
让出CPU执行权(yeild)
Thread类的静态方法
当一个线程调用yield方法时,当前线程会让出CPU使用权,然后处于就绪状态,线程调度器会从线程就绪队列里面获取一个线程优先级最高的线程,当然也有可能会调度到刚刚让出CPU的那个线程来获取CPU执行权。
线程中断(interrupt)
| 方法 | 作用 |
|---|---|
| void interrupt | 中断线程 |
| boolean isinterrupted() | 检测当前线程是否被中断 |
| Boolean interrupted() | 检测当前线程是否被中断(static方法、发现线程被中断会清除中断标志) |
线程的上下文切换
CPU资源分配采用时间片轮转策略
线程上下文切换时机有:
1、当前线程的CPU时间片使用完处于就绪状态时
2、当前线程被其他线程中断时。
线程死锁
为什么会形成死锁?
两个或两个以上的线程在竞争资源的过程中造成的相互等待的现象。
死锁产生的四个条件:
1、互斥条件:线程对已经获得的资源进行排他性使用,即该资源同时只由一个线程占用
2、请求并持有条件:指一个线程已经持有资源,并提出新的资源请求,而新资源已经被其他线程占有,当前线程会阻塞,但并不释放自己已经获得的资源。
3、不可剥夺条件:线程获得的资源不能由其他线程抢占,只能自己执行完了进行释放。
4、环路等待条件:存在线程-资源的环形链
如何避免死锁?
至少要破坏一个死锁产生的条件,其中,只有条件2和4能被破坏。
资源申请的有序性
用户进程Vs守护进程
并发Vs.并行
| 并发 | 同一个时间段内多个任务同时都在执行,并且都没有执行结束 |
|---|---|
| 并行 | 在单位时间内多个任务同时在执行 |
并发任务强调在一个时间段内同时执行,而一个时间段由多个单位时间累积而成,所以说并发的多个任务在单位时间内不一定同时在执行。
并发(单CPU例子):
并行:
