一 基础
进程是程序向操作系统申请资源(如内存空间和文件句柄)的基本单位。线程是进程中可独立执行的最小单位。
在Java平台中创建一个线程就是创建一个Thread类(或其子类)的实例。
运行一个线程实际上就是让Java虚拟机执行该线程的run方法,从而使相应线程的任务处理逻辑代码得以执行。
Thread类的两个常用构造器是:Thread() 和 Thread(Runnable target) 。(Thread的run方法里会判断target !=null,所以Tread类实际上是Runnable接口的一个实现类。)
如:
Thread testThread = new TestThread(); class TestThread extends Thread Thread testThread = new Thread(new TestThread()); class TestThread implements Runnable
testThread.start(),class里重写run方法,run里是自己的逻辑代码。run方法总是由Java虚拟机直接调用,我们能直接调,但这蠢事没人干。
我们一般使用第二种方法创建线程,因为它基于组合,比继承灵活。但是,第二种创建方式意味着多个线程实例可以共享同一个Runnable实例,这可能因此竞态和线程安全相关问题。
run方法执行结束,线程占用的资源会被Java虚拟机回收。(线程池的原理是让run方法永远运行不完么)
由于同一段代码可以被多个线程执行,因此当前线程是相对的。
线程的优先级属性本质上只是一个给线程调度器的提示信息。
Java中的线程分为守护线程和用户线程。
用户线程会阻止Java虚拟机的正常停止,即一个Java虚拟机只有在其所有用户线程都运行结束的情况下才能正常停止。如果用kill命令杀Java虚拟机进程,那即使用户线程也无法阻止Java虚拟机的停止。
而守护线程不会影像Java虚拟机的正常停止,即应用程序中的守护线程在运行也不影响Java虚拟机的正常停止。因此,守护线程通常用于执行一些重要性不高的任务,例如用于监视其他线程的运行情况。
常用方法:run 虚拟机调用;start 一个thread实例的start方法只能被调一次,多了会异常;join 线程a调用线程b的join方法,线程a的运行会被暂停,直到线程b运行结束;yield 使当前线程可能主动放弃对处理器的占用,导致当前线程被暂停,相当于对程序调度器说:我不急,有人用就先让别人用,真没人用就我用; sleep 使当前线程休眠指定时间。
Java虚拟机启动的时候会创建一个main线程,该线程负责执行Java程序的入口方法(main方法)。web应用中的servlet类的doget、dopot方法也总是由确定的线程负责执行的。垃圾回收有专门的线程。动态编译有专门的线程。在多线程编程中,弄清楚一段代码具体是由哪个或哪种线程去负责执行的这点很重要,这关系到性能、线程安全等问题。
线程的生命周期共有6种状态,new,runnable,blocked,waiting,timed_waiting,terminated。
对线程进行监视的主要途径是获取并查看程序的线程转储(Thread Dump),它包含了获取这个线程转储的那一刻该程序的线程信息。
从软件的角度来说,并发就是在一段时间内以交替的方式去完成多个任务,而并行就是以齐头并进的方式去完成多个任务。软件要以并发的方式去完成几个任务往往需要借助多个线程(而不是一个线程)。
从硬件的角度来说,一个处理器一次只能运行一个线程,处理器是通过时间片分配实现同一段时间运行多个线程,因此一个处理器可以实现并发,而并行需要多个处理器。
竞态:结果有时对,有时错,正确性和时间有关。比如多个线程取到相同的值,就是因为一个线程对共享值的更新覆盖了其他线程对该值的更新。所以,竞态往往伴随着读取脏数据,和丢失更新。
synchronized关键字会使其修师的方法在任一时刻只能够被一个线程执行。
一个类如果不是线程安全的,我们就说它在多线程环境下直接使用存在线程安全问题。线程安全问题概况来说表现为3个方面:原子性、可见性和有序性。
原子性的不可分割:线程执行某个共享变量,对其他线程来说,该操作要么已经执行结束要么尚未发生。
Java有两种方式实现原子性,一种是使用锁,另一种是利用处理器提供的专门CAS执行。CAS与锁本质相同,差别在于锁在软件层实现,CAS在硬件层实现(处理器和内存)。
Java语言除了long和double,其他基本类型的写操作都是原子操作,有Java虚拟机具体实现。
原子性指操作不能被中断,可见性指多线程之间对共享变量的修改是否进行了处理器的缓存同步而被其他线程实时读取到,有序性指源码顺序和处理器执行顺序可能不一样,反正这三个在Java中都可以通过volatile修饰来避免线程安全问题。
我不想做笔记了,用别人的吧。Java多线程编程实战指南(核心篇)读书笔记;
补充一点——Java多线程程序的调试与测试:
针对多线程程序的测试框架和工具还没有,而junit也不支持测试多线程。能用静态检查工具FindBugs;但它会有误报。
或者直接代码审查;
OpenJDK下有一个多线程程序单元测试的试验项目:JCStress;