做为Android开发者,老实说,日常关于一些线程调度的方法,用的确实很少,可能用的最多的也就是sleep做为一个休眠延时的操做,可是既然是Java之路,那就必须把那些东西拎出来讲一说了,也是增强你们对线程的理解程度以及在处理线程中应该注意的问题。java
1.join() 等待线程终止android
这个方法你们可能用的很少,咱们想象一个场景:主线程生成并起动了子线程,若是子线程里要进行大量的耗时的运算,主线程每每将于子线程以前结束,可是若是主线程处理完其余的事务后,须要用到子线程的处理结果,也就是主线程须要等待子线程执行完成以后再结束,这个时候咱们第一想法多是要否则在子线程中处理完以后,用Handler把消息传到主线程再处理?这样每每比较麻烦,这个时候就能够用join方法来实现面试
public class TestClass {
public static void main(String []agrs){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("child thread start");
try {
//模拟耗时操做
Thread.sleep(3000);
} catch (InterruptedException mE) {
mE.printStackTrace();
}
System.out.println("child thread over");
}
});
thread.start();
try {
thread.join();
} catch (InterruptedException mE) {
mE.printStackTrace();
}
System.out.println("main thread call");
}
}
复制代码
运行,打印一波,能够看到调用join方法以后,主线程就能够在主线程执行完成以后,处理逻辑了bash
child thread start
child thread over
main thread call
Process finished with exit code 0
复制代码
2.wait()/notifyAll-----经典的生产者消费者问题ide
话很少说,直接上代码oop
public class Model {
//为了触发阻塞状态,这里把最大容量设置为1
public static final int MAX_SIZE = 1;
//存储数据的集合
public static LinkedList<Integer> list = new LinkedList<>();
class Producer implements Runnable {
@Override
public void run() {
synchronized (list) {
//仓库容量已经达到最大值
while (list.size() == MAX_SIZE) {
System.out.println(Thread.currentThread().getName() + " no need to produce! repertory is full");
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.add(1);
System.out.println( Thread.currentThread().getName() + " produce,current repertory is " + list.size());
list.notifyAll();
}
}
}
class Consumer implements Runnable {
@Override
public void run() {
synchronized (list) {
while (list.size() == 0) {
System.out.println(Thread.currentThread().getName() + " no product to consume! repertory is empty ");
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.removeFirst();
System.out.println(Thread.currentThread().getName() + " consume,current repertory is " + list.size());
list.notifyAll();
}
}
}
}
复制代码
主要代码就是调用wait/notifyAll方法,分别在极限状况时,对线程进行挂起以及唤醒,消费者和生产者开启10个线程来测试一波post
public class TestClass {
public static void main(String []agrs){
Model model = new Model();
Model.Producer producer = model.new Producer();
Model.Consumer consumer = model.new Consumer();
for (int i = 0; i < 10; i++) {
Thread proThread = new Thread(producer);
proThread.start();
Thread conThread = new Thread(consumer);
conThread.start();
}
}
}
复制代码
Thread-0 produce,current repertory is 1
Thread-2 no need to produce! repertory is full
Thread-1 consume,current repertory is 0
Thread-2 produce,current repertory is 1
Thread-3 consume,current repertory is 0
Thread-5 no product to consume! repertory is empty
Thread-4 produce,current repertory is 1
Thread-5 consume,current repertory is 0
Thread-7 no product to consume! repertory is empty
Thread-6 produce,current repertory is 1
Thread-7 consume,current repertory is 0
Thread-8 produce,current repertory is 1
Thread-9 consume,current repertory is 0
Thread-10 produce,current repertory is 1
Thread-11 consume,current repertory is 0
Thread-12 produce,current repertory is 1
Thread-13 consume,current repertory is 0
Thread-14 produce,current repertory is 1
Thread-15 consume,current repertory is 0
Thread-16 produce,current repertory is 1
Thread-17 consume,current repertory is 0
Thread-18 produce,current repertory is 1
Thread-19 consume,current repertory is 0
Process finished with exit code 0
复制代码
能够看到,当Thread-2要去生产时,发现此时仓库以及满了,此时调用wait方法,释放锁,同时线程阻塞,注意,这里线程并不会结束掉,只是出于挂起状态,当下次被唤醒时,会沿着wait方法后面继续执行,在第四行也能够看到,当Thread-1消费了的时候,会调用notifyAll,此时唤醒全部在锁池的对象,从新竞争获取锁,此时Thread-2又开始生产了测试
这种方法现象在Java中很常见,好比上篇线程池的文章也有提到过,里面使用的阻塞队列底层采用也是相似的机制,核心线程不会被回收被挂起,当有任务来时,唤醒线程去执行,有兴趣的能够去看看重走JAVA之路(五):面试又被问线程池原理?教你如何反击spa
再次总结一下:线程
咱们知道android是基于消息机制的,像以前的一个问题为何Looper.loop()死循环不会致使ANR同样,主线程从队列中读取消息,当没有消息时,主线程阻塞,让出CPU,当消息队列中有消息时,唤醒主线程,接着处理数据,因此 Looer.loop()方法可能会引发主线程的阻塞,但只要它的消息循环没有被阻塞,能一直处理事件就不会产生ANR异常。
3.interrupt() 中止线程
当须要终止一个线程时,Java给咱们提供了2中方法,stop/interrupt,前者已经被废弃了,也是不提倡调用的,一调用该方法,被stop的线程会立刻会释放全部获取的锁并在线程的run()方法内,任何一点都有可能抛出ThreadDeath Error,包括在catch或finally语句中,那么很容易照成被同步的数据没有被正确的处理完,那么其它线程在读取时就会获得脏数据
这里主要讲解interrupt方法, 首先咱们要明白一点:
调用interrupt()方法,马上改变的是中断状态,但若是不是在阻塞态,就不会抛出异常;若是在进入阻塞态后,中断状态为已中断,就会马上抛出异常,什么叫阻塞态呢,大概就是调用了sleep,join,wait这几个方法,其实在源码方法注释上面也能够看到这些解释,若是是非阻塞态的话,那其实这个方法是不起做用的,什么,不信?那来测试下
public class TestClass {
public static void main(String []agrs){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true){
System.out.println("run");
}
}
});
thread.start();
thread.interrupt();
}
}
复制代码
run
run
run
run
run
run
run
复制代码
果真,是不起做用的,那咱们再加上阻塞状态sleep,试一下
public class TestClass {
public static void main(String []agrs){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true){
System.out.println("run");
try {
Thread.sleep(1000);
} catch (InterruptedException mE) {
mE.printStackTrace();
return;
}
}
}
});
thread.start();
thread.interrupt();
}
}
复制代码
run
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.example.hik.lib.MyClass$1.run(MyClass.java:12)
at java.lang.Thread.run(Thread.java:748)
复制代码
能够看到,在Thread.sleep的方法,抛出了异常,同时return掉,此时才是中止了线程,咱们根据捕获异常实现逻辑,若是没法肯定逻辑,那就直接抛出,由上层去处理。
4.总结
须要注意的一点,wait/notify是Object的方法,其余是Thread的方法,由于每一个对象都有内置锁,主要目的仍是理解下线程中的一些状态以及阻塞状态的本质,但愿可以帮助到你们,若有疑问或者错误,欢迎一块儿讨论