什么是线程
现代的操作系统运行一个程序时,会为其创建一个进程。在一个进程里可以创建多个线程,线程是操作系统调度的最小单元,也叫轻量级进程,这些线程拥有各自的计数器、堆栈和局部变量,并且能够共享内存变量。
现在操作系统基本采用时分的形式调度运行的线程,操作系统会分出一个个时间片,线程会分配到若干个时间片,当线程的时间片用完了就会发生线程的调度,并等待下次分配。线程优先级越高线程分配的时间片数量就越多。
线程的状态
状态名称 | 说明 |
---|---|
NEW | 初始状态,线程被构建,但还没有调用start()方法 |
RUNNABLE | 运行状态,java线程将操作系统中的就绪和运行两种状态称作运行中 |
BLOCKED | 阻塞状态,表示线程阻塞于锁 |
WAITING | 等待状态,表示线程进入等待状态,进入等待状态表示该线程需要等待其他线程做出一些动作(通知或中断) |
TIME_WAITING | 超时等待状态,该状态不同于WAITING,它是可以指定时间自行返回的 |
TERMINATED | 终止状态,表示当前线程已经执行完毕 |
java线程状态变迁如下图
daemon线程
daemon属性需要在线程启动之前设置,不能在启动线程之后设置。档一个java虚拟机中不存在非daemon线程的时候,虚拟机就会推出,因此不能在daemon线程中使用finally块中开确保执行关闭或清理资源。
启动和终止线程
java中新构建的线程是由其parent线程来进行空间分配,而child线程继承parent是否为Daemon、优先级和加载资源的contextClassLoader以及可继承的ThreadLocal。同时还会分配一个唯一的ID标识这个child线程。线程初始化过程见方法Thread.init()
调用start()方法开启线程,及告知java虚拟机,只要线程规划器空闲,应立即启动调用start()方法的线程。
线程通过方法isInterruoted()来判断是否被中断。也可以调用静态方法Thread.interrupted()对当前线程的终端标识进行复位。若果一个线程已经处于终结状态,该线程的状态是false;java中许多声明InterruptedException的方法,这些方法抛出异常之前,JAVA虚拟机会将该线程的终端表示为清除,让后抛出InterruptedException,此时调用isInterrupted()方法将返回false。
线程间通信
volatile和synchronized关键字
使用volatile关键字来修饰成员变量,就告知程勋任何对该成员变量的访问均需从共享内存中获取,而对它的改变必须同步刷新回共享内存,他能保证所有线程对变量访问的可见性。
synchronized关键字可以保证多线程同一时刻,只能有一个线程处于方法或同步块中,它保证了线程对变量访问的可见性和排他性。执行线程必须获取到对象的监视器再能进入同步方法中,而没有获取到监控对象监视器的线程会被阻塞,进入BLOCKED状态,线程会进入同步阻塞队列,当访问对象的线程释放锁,该释放锁操作会唤醒同步阻塞队列中的线程,使其重新尝试对监视器获取。
等待/通知机制
方法名称 | 描述 |
---|---|
notify() | 通知一个对象上等待的线程,使其从wait()方法返回,而返回的前提是该线程获取到了对象的锁 |
notifyAll() | 通知所有等待该对象上的线程 |
wait() | 调用该方法的线程进入WAITING状态,只有等待另外线程通知或被终端才会返回,该方法会释放所对象 |
wait(long) | 超时等待一段时间,这里的时间参数是毫秒,也就是等待n毫秒,如果没有通知就超时返回 |
wait(long,int) | 对超时时间更细力度的控制,可达到纳秒 |
当线程执行同步代码时,会尝试或同步对象的监控对象,获取失败的时候将进入同步等待队列(synchroizedQueue),进入阻塞状态。获取成功之后,进入同步代码,如果调用wait()方法,将释放锁并进入锁对象的等待队列(WaitQueue),进入等待状态。如果所对象调用notify()或notifyAll()方法,该所等待队列中的线程将进入同步等待队列。锁释放会唤醒该所同步等待队列中的线程,使其重新尝试获取所对象的监视器。
Thread.join()的使用
ThreadLocal的使用
threadlocal而是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据。set()设置值get()获取值