Java线程基本概念
进程:进程是操作系统中资源的组织单位,包含了程序内容和数据的地址空间,已经其它的资源,包括打开的文件、子进程和信号处理器等。不同的进程的地址空间是隔离的
线程:线程表示的是程序的执行流程,是CPU调度的基本单位,线程有自己的程序计数器、寄存器、栈和帧等。引入线程的动机是因为操作系统中阻塞式I/O,使得应用的执行效率有了很大的提高
Java使用java.lang.Thread类对线程进行抽象。创建一个新线程有两种方法,一种是直接继承Thread类,一种是让Thread类接收一个实现了Runnable接口的类,只要调用start()方法就可以启动一个线程。一个线程可能有几种状态,可能正在被CPU调度,也有可能处于就绪状态等待CPU的调度,也有可能阻塞在某个事件或资源上。多个处于就绪状态会竞争CPU运行时间,CPU采用某种调度算法进行调度,线程的运行顺序是不能够确定的
Java线程的可见性
在单线程程序中,首先改变一个变量的值,再去读取它,所取到的值就是前一次写入的值,也就是说前一次的写入对后一次的读取是可见的。但在多线程程序中就不能保证一个线程写入的值对另外一条线程是可见的,造成的原因可能有一下几方面:
不同的CPU可能采用不同的架构,在多核多处理器这样的问题尤为明显,Java需要实现跨平台,就有必要对Java程序访问和操作主存的方式作出规范以保证Java程序在不同的平台上运行的结果是一样的,因而有了Java内存模型
Java内存模型
Java内存模型描述了程序中共享变量的关系以及主存对这些变量写入和读取的底层细节,Java中定义了一些synchronized、volatitle、final等关键词,让开发者能够告诉JVM自己的期望,JVM负责保证程序运行符合内存模型的描述,synchronized、volatitle关键词能够确保"在之前发生"的顺序,能够使变量在线程间是可见的
Java中的锁
当数据产生竞争时,最简单的做法就是加锁,锁机制可以确保同一时间只允许一条线程访问产生竞争数据的临界区,Java中可以用synchronized关键字对方法和代码块进行加锁。任何对象都会有个对自己的监视器,可以进行加锁和解锁操作。线程可以获取对象监视器上的锁,锁的释放:方法或代码块执行完成和程序异常退出会自动释放对象锁,可以对一个对象加多把锁,JVM会确保加锁之前和解锁之后变量的值是与主存中的内容是对应的
Java线程同步
有些时候仅依靠线程间对数据的互斥访问是不够的,线程之间有可能需要按照某种规则协同合作,就比如生成者-消费者模式,这个时候就需要用到线程间的等待-通知机制。java.lang.Object中的wait/notify/notifyAll用来完成线程间同步的,调用wait()方法需要已经获取到对象上的锁,否则抛出异常,之后线程释放其所拥有的锁,并加入到对象的等待集合中,线程处于阻塞状态,直到被移除出出对象的等待集合。
引起某个线程从对象等待集合中移除的原因:对象上调用notify(),该线程被选上;对象上调用notifyAll()方法;线程被中断;对有超时限制的wait,超时时间到;JVM内部实现在非正常情况下的操作
线程有可能在非正常的情况下被唤醒,一般把wait操作放到一个循环中,并检查所要求的逻辑条件是否满足,典型的写法:
private Object lockObj = new Object();
//使用私有的Object来加锁,可以避免其他对象错误使用
synchronized(lockObj){
while(/*逻辑条件不满足*/){
try{
lock.wait();
}catch(
IllegalMonitorStateException e){}
}
}
中断线程
stop()和interrupt方法总感觉不是那么可靠,我们可以这样来中段一个线程
中断线程最好的,最受推荐的方式是,使用共享变量(shared variable)发出信号,告诉线程必须停止正在运行的任务。线程必须周期性的核查这一变量(尤其在冗余操作期间),然后有秩序地中止任务。
volatitle isStop;
while(isStop){
//do some thing
}