线程复习笔记
内容主要来自《Java多线程编程核心技术》 高洪岩著
1.线程的生命周期
线程是一个动态执行的过程,它也有一个从产生到死亡的过程。
(1)生命周期的五种状态
新建(new Thread)
当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。
例如:Thread t1=new Thread();
就绪(runnable)
线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源。例如:t1.start();
运行(running)
线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。
死亡(dead)
当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。
自然终止:正常运行run()方法后终止
异常终止:调用stop()方法让一个线程终止运行
堵塞(blocked)
由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。
正在睡眠:用sleep(long t) 方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。
正在等待:调用wait()方法。(调用motify()方法回到就绪状态)
被另一个线程所阻塞:调用suspend()方法。(调用resume()方法恢复)
2.常用方法
void run() 创建该类的子类时必须实现的方法
void start() 开启线程的方法
static void sleep(long t) 释放CPU的执行权,不释放锁
static void sleep(long millis,int nanos)
final void wait()释放CPU的执行权,释放锁
final void notify()
static void yied()可以对当前线程进行临时暂停(让线程将资源释放出来)
3.(1)结束线程原理:就是让run方法结束。而run方法中通常会定义循环结构,所以只要控制住循环即可
(2)方法—-可以boolean标记的形式完成,只要在某一情况下将标记改变,让循环停止即可让线程结束
(3)public final void join()//让线程加入执行,执行某一线程join方法的线程会被冻结,等待某一线程执行结束,该线程才会恢复到可运行状态
引用出处:http://blog.csdn.net/mayouarebest8621/article/details/6755036
2.方法
currentThread():返回调用代码段的线程信息
isAlive():判断线程是否处于活动状态,start之前调用为false,start之后为true
sleep():指定毫秒数让当前线程休眠
getId():取得线程的唯一标识
suspend():挂起(也是有死锁的隐患,和锁有关,废除)
resume():重新开始执行,(和suspend,因为很容易造成资源独占,导致死锁,废除)
stop():停止(一般线程结束都是在线程中通过流程控制来结束,因为用stop太过暴力,会释放线程所有保有的资源,废除)
setPriority():设置线程的优先级
join(重要):当前线程等待新开的线程结束后再结束,多用于主线程和子线程
子线程.start()
子线程.join()
join(long),延时,内部用wait()释放锁
sleep(long),不释放锁
join方法会阻塞线程,所以一个线程不能同时开启多个线程然后join,这样用非但不能达到目的,反而会使逻辑变的混乱。
3.对象监视器(锁)
方法内变量为线程安全,实例中变量非线程安全(多个线程可能会访问同一个实例中的变量)
synchronized修饰方法,先调用该方法在没执行完时会持有锁,使后调用该方法的线程阻塞,直到前一个线程将锁释放。
synchronized(this)同步代码块,两个线程同时访问一个对象中的同步代码块时,先进入线程释放锁之前,后访问的线程阻塞等待。
synchronized同步方法和synchronized(this)互相阻塞
synchronized(非this对象)互相阻塞,互相阻塞表示的是对象监视器相同,在一个线程持有锁,另一个线程调用具有加锁的代码块时就会等待,wait()方法会释放当前线程的锁,sleep()不会释放锁
static方法加synchronized持有的是class锁,而普通方法加synchronized持有的是对象锁,所以两者不能同步
原子性
原子性就是操作的不可分割性,就和加上同步是一个效果,一个线程对一些变量进行操作,操作没有完成就不能有其他线程操作,否则,回写的值就会出现问题,产生脏数据。
volatile是强制从公共堆栈中取得变量,而不是从线程专有的线程中取得,对它的操作不具备原子性,也就是在不同线程同时写入可能会出错。
原子类如:AtomicInteger,前有前缀Atomic
原子类的操作也不一定是线程安全的,因为之外可能有不安全的操作,要综合考虑。
等待通知
锁对象所属的wait()和notify()方法是等待和唤醒,在同步代码块之中,如果所属同一个锁对象的wait()被多个线程调用,那么调用的线程会释放对象锁,然后进入线程等待池中,等待被唤醒。如果调用notify()会随机唤醒锁对象,多调用几次就能唤醒全部,也可以直接调用notifyAll()
ThreadLocal和InheritableThreadLocal
InheritableThreadLocal继承于前者
#同步锁
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock();
condition.await()
condition.signal()
condition.signalAll()
lock相当于之前的synchronized
condition相当于之前的锁对象,里面有相关的等待唤醒方法。只是功能更强大。可以建立多个来保证等待与唤醒线程的准确切换。
公平锁与非公平锁:传入一个boolean来指定
tryLock()立即返回,可以带延时
lock()获取不到,就使当前线程休眠等待
ReentrantReadWriteLock读写锁
读读共享,写写互斥,读写互斥,写读互斥
#线程组与线程池
线程组是为了方便线程的管理
线程池是为了减少线程创建与销毁的开销
单例模式
饿汉模式:直接建立对象,获取直接返回
懒汉模式:为了解决多线程下问题,需要对对象进行双检查,既防止不同线程多次创建实例,又保证了效率
if(obj!=null){
}else{
synchronized(){
if(obj==null)
obj = new Obj();
}
}
守护线程
在新建的线程启动前setDaemon(true)即可,在守护线程中新开的线程也是守护线程,守护线程不应访问资源,因为守护线程有中断的危险。当JVM中不存在非守护线程时,就会销毁JVM。