一、线程相关的基本方法有wait,notify,notifyAll,sleep,join,yield等。
- 使用wait/notify实现线程间通信
作用:减少cpu资源浪费等待用时实现多个线程间的通信
作用范围:用于同步块与同步方法中
属于Object类
wait()
使线程停止运行,由running进入Blocked状态
立即释放对象锁
wait()状态遇到interrupt会抛异常
wait(long)当指定时间不被唤醒时自动唤醒,类似sleep()
notify()
是停止的线程继续运行,由Blocked进入runnable状态
等到同步方法或同步块执行完毕才释放锁,不会自动释放
每次随机通知一个线程,获取锁
notifyAll()
通知所有wait该对象的线程
子主题 注意等待条件发生变化造成的程序逻辑混乱
通知的协调性,过早会使当前线程一直阻塞
三种释放锁的的实验
程序执行完同步方法释放
程序在执行同步方法中遇到异常释放
执行wait()方法后
每个锁对象都有两个队列,一个是阻塞队列,一个是就绪队列
- 线程睡眠(sleep)
sleep导致当前线程休眠,与wait方法不同的是sleep不会释放当前占有的锁,sleep(long)会导致线程进入TIMED-WATING状态,而wait()方法会导致当前线程进入WATING状态
- 线程让步(yield)
yield会使当前线程让出CPU执行时间片,与其他线程一起重新竞争CPU时间片。一般情况下,优先级高的线程有更大的可能性成功竞争得到CPU时间片,但这又不是绝对的,有的操作系统对线程优先级并不敏感。
- 线程中断(interrupt)
中断一个线程,其本意是给这个线程一个通知信号,会影响这个线程内部的一个中断标识位。这个线程本身并不会因此而改变状态(如阻塞,终止等)。
1.调用interrupt()方法并不会中断一个正在运行的线程。也就是说处于Running状态的线程并不会因为被中断而被终止,仅仅改变了内部维护的中断标识位而已。
2.若调用sleep()而使线程处于TIMED-WATING状态,这时调用interrupt()方法,会抛出InterruptedException,从而使线程提前结束TIMED-WATING状态。
3.许多声明抛出InterruptedException的方法(如Thread.sleep(long mills方法)),抛出异常前,都会清除中断标识位,所以抛出异常后,调用isInterrupted()方法将会返回false。
4.中断状态是线程固有的一个标识位,可以通过此标识位安全的终止线程。比如,你想终止一个线程thread的时候,可以调用thread.interrupt()方法,在线程的run方法内部可以根据thread.isInterrupted()的值来优雅的终止线程。
- Join等待其他线程终止
Join等待其他线程终止join() 方法,等待其他线程终止,在当前线程中调用一个线程的join() 方法,则当前线程转为阻塞状态,回到另一个线程结束,当前线程再由阻塞状态变为就绪状态,等待cpu 的宠幸。
为什么要用join()方法?
很多情况下,主线程生成并启动了子线程,需要用到子线程返回的结果,也就是需要主线程需要在子线程结束后再结束,这时候就要用到join() 方法。
System.out.println(Thread.currentThread().getName() + "线程运行开始!");
Thread6 thread1 = new Thread6();
thread1.setName("线程B");
thread1.join();
System.out.println("这时thread1执行完毕之后才能执行主线程");
二、其他方法
- sleep():强迫一个线程睡眠N毫秒。
- isAlive():判断一个线程是否存活。
- activeCount():程序中活跃的线程数。
- enumerate():枚举程序中的线程。
- currentThread():得到当前线程。
- isDaemon():一个线程是否为守护线程。
- setDaemon():设置一个线程为守护线程。(用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束)
- setName():为线程设置一个名称。
- setPriority():设置一个线程的优先级。
- getPriority()::获得一个线程的优先级。
三、线程上下文切换
巧妙地利用了时间片轮转的方式, CPU给每个任务都服务一定的时间,然后把当前任务的状态保存下来,在加载下一任务的状态后,继续服务下一任务,任务的状态保存及再加载, 这段过程就叫做上下文切换。时间片轮转的方式使多个任务在同一颗CPU上执行变成了可能。
- 进程
(有时候也称做任务)是指一个程序运行的实例。在Linux系统中,线程就是能并行运行并且与他们的父进程(创建他们的进程)共享同一地址空间(一段内存区域)和其他资源的轻量级的进程。
- 上下文
是指某一时间点CPU 寄存器和程序计数器的内容。
- 寄存器
是CPU 内部的数量较少但是速度很快的内存(与之对应的是CPU 外部相对较慢的RAM 主内存)。寄存器通过对常用值(通常是运算的中间值)的快速访问来提高计算机程序运行的速度。
- 程序计数器
是一个专用的寄存器,用于表明指令序列中CPU 正在执行的位置,存的值为正在执行的指令的位置或者下一个将要被执行的指令的位置,具体依赖于特定的系统。
- PCB-“切换桢”
上下文切换可以认为是内核(操作系统的核心)在CPU 上对于进程(包括线程)进行切换,上下文切换过程中的信息是保存在进程控制块(PCB, process control block)中的。PCB还经常被称作“切换桢”(switchframe)。信息会一直保存到CPU的内存中,直到他们被再次使用。
- 上下文切换的活动
1.挂起一个进程,将这个进程在CPU 中的状态(上下文)存储于内存中的某处。
2.在内存中检索下一个进程的上下文并将其在CPU 的寄存器中恢复。
3.跳转到程序计数器所指向的位置(即跳转到进程被中断时的代码行),以恢复该进程在程序中。
- 引起线程上下文切换的原因
1.当前执行任务的时间片用完之后,系统CPU正常调度下一个任务;
2.当前执行任务碰到IO阻塞,调度器将此任务挂起,继续下一任务;
3.多个任务抢占锁资源,当前任务没有抢到锁资源,被调度器挂起,继续下一任务;
4.用户代码挂起当前任务,让出CPU时间;
5.硬件中断;