线程
线程的状态
-
新状态:线程对象已经创建,还没有在其上调用start()方法。
-
就绪:当线程有资格运行,但调度程序还没有把它选定为运行线程,这时候线程所处的状态就做就绪状态。当start()方法调用时,线程首先进入可运行状态。在线程运行之后或者从阻塞、等待或睡眠状态回来后,也返回到可运行状态。另外yield可以让线程由“运行状态”进入到“就绪状态”。
-
运行:线程调度程序从可运行池中选择一个线程来执行。这也是线程进入运行状态的唯一一种方式。
-
等待/阻塞/睡眠:这是线程有资格运行时它所处的状态。实际上这个三状态组合为一种,其共同点是:线程仍旧是活的,但是当前没有条件运行。换句话说,它是可运行的,但是如果某件事件出现,他可能返回到可运行状态。另外wait可以让线程由“运行状态”进入到“等待(阻塞)状态”。
-
死亡态:当线程的run()方法完成时就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。 如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
线程方法
1. notify/notifyAll
- notifyAll注释为
Wakes up all threads that are waiting on this object's monitor. A thread waits on an object's monitor by calling one of the {@code wait} methods.
This method should only be called by a thread that is the owner
of this object's monitor.
一旦一个对象调用了wait方法,必须要采用notify()和notifyAll()方法唤醒该进程;
关于线程的常见问题
1. Object的wait和notify/notifyAll如何实现线程同步?
在Object.java中,定义了wait(), notify()和notifyAll()等接口。
wait()的作用是让当前线程进入等待状态,同时,当object调用wait()方法会让当前线程释放它所持有object对象锁。
而notify()和notifyAll()的作用,则是唤醒在此对象监视器上等待的单个/全部线程。
a.关于wait的一个错误案例:
final Object object = 1;
new Thread(){
@Override
public void run() {
synchronized (object){
Log.e("jack",Thread.currentThread().getName()+"->"+1);
try {
/**
* object not locked by thread before wait(),
* 这是因为我们锁住的是object对象,但是wait,相当于this.wait,解锁的是当前对象。
* 当前对象并没有locked,所以会有这个错误。
*/
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("jack",Thread.currentThread().getName()+"->"+2);
}
}
}.start();
b.关于notify的一个案例,注意同步对象:
//是否满足某种条件的标识
boolean flag = false;
public void onWait(View view) {
//每次点击初始化 falg 的值
flag = false;
final Thread t1 = new Thread("Thread 1") {
@Override
public void run() {
/**
* 这边锁住的是 MainActivity 的对象。
* 在run方法里通过 wait 释放 MainActivity对象锁,如果想要再次唤起这个线程,肯定还是
* MainActivity.this.notify();
*
* 如果这边同步this,则代表当前线程对象。
*/
synchronized (MainActivity.this) {
//不满足条件就在这里等待
while (!flag) {
Log.e("jack", Thread.currentThread().getName() + "->" + 1);
try {
MainActivity.this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("jack", Thread.currentThread().getName() + "->" + 2);
}
}
}
};
t1.start();
Thread t2 = new Thread("Thread 2") {
@Override
public void run() {
try {
//保证"Thread 1"先执行
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (MainActivity.this) {
try {
Log.d("jack", Thread.currentThread().getName() + "->" + 3);
Thread.sleep(1000);
flag = true; //满足了某种条件
/**
* 唤醒在 MainActivity.this 对象监视器上等待的单个/全部线程。
*/
MainActivity.this.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d("jack", Thread.currentThread().getName() + "->" + 4);
}
}
};
t2.start();
}
Paste_Image.png
线程1执行打印了 "Thread 1->1",然后通过 MainActivity.this.wait();释放了当前线程所持对象的锁...
2. wait和yield(或sleep)的区别?
wait()是让线程由“运行状态”进入到“等待(阻塞)状态”
yield()是让线程由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程有机会获取执行权;但是,并不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权。
wait()是会在线程释放它所持有对象的同步锁,而yield()方法不会释放锁。
Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)静态方法强制当前正在执行的线程休眠.当线程睡眠时,它入睡在某个地方,在苏醒之前不会返回到可运行状态。当睡眠时间到期,则返回到可运行状态。
线程同步
1. wait notify 来实现
以android中的HandlerThread的源码为例说明
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
/**
* 获取到当前Thread的Looper对象
* 然后通知 getLooper 方法,这个时候可以返回(同步)
*/
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
/**
* 等待run方法的Looper对象初始化完成
*/
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
2. synchronized
a. 说一下 synchronized与static synchronized 的区别
synchronized是对类的当前对象进行加锁,防止其他线程同时访问该对象的所有synchronized块,其实synchronized就相当于this.synchronized。
static synchronized恰好就是要控制类的所有对象的访问了,static synchronized的作用是同步线程访问该类的所有对象代码块。
3. CountDownLatch
一个同步辅助类(大名鼎鼎的java.util.concurrent包),在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
4. 重入锁ReentrantLock
- 重入性:指的是同一个线程多次试图获取它所占有的锁,请求会成功。当释放锁的时候,直到重入次数清零,锁才释放完毕。
- ReentrantLock将由最近成功获得锁,并且还没有释放该锁的线程所拥有。如果当前线程已经拥有该锁,此方法将立即返回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法来检查此情况是否发生。
- 此类的构造方法接受一个可选的公平参数。当设置为 true 时,在多个线程的争用下,这些锁倾向于将访问权授予等待时间最长的线程。否则此锁将无法保证任何特定访问顺序。
- 常用方法
lock
unlock
isHeldByCurrentThread
getHoldCount