android thread知识你还记得多少?
Posted android超级兵
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android thread知识你还记得多少?相关的知识,希望对你有一定的参考价值。
android 线程知识你还记得多少?
- 常见创建线程的几种方式
- Thread.start() 和 Thread.run() 的区别
- Thread.join() 线程串形化
- Thread.Interrupt() 线程中断
- Thread 数据共享 synchronized(隐式锁)
- Thread线程隔离(ThreadLocal)
- notify(),notifyAll()和wait()的使用
- 显示锁
- 课外知识 volatile!
常见创建线程的几种方式
-
方式一:
-
方式二:
-
方式三:
方式一,方式二和方式三的启动:
public static void main(String[] args) throws ExecutionException, InterruptedException
// 方式一
MyThread myThread = new MyThread();
myThread.start();
// 方式二
MyRunnable myRunnable = new MyRunnable();
new Thread(myRunnable).start();
// 方式三
MyCallable myCallable = new MyCallable();
FutureTask<String> futureTask = new FutureTask<String>(myCallable)
@Override
protected void done()
System.out.println("方式三开始前!");
super.done();
System.out.println("方式三执行后!");
try
System.out.println("done内部获取方式三返回结果:" + get());
catch (InterruptedException e)
e.printStackTrace();
catch (ExecutionException e)
e.printStackTrace();
;
// 方式三启动线程
new Thread(futureTask).start();
System.out.println("获取方式三返回结果:" + futureTask.get());
运行结果:
三种方式的区别:
- extend Thread 需要手动重写
run
方法 - implements Runnable 需要将当前
runnable
对象传入Thread
中 - implements Callable<*> 可以获取到
thread
的返回值,并且不能直接传给thread启动,需要采用FutureTask
来配合,并且可以通过FutureTask.get()
获取到实现implements Callable的<*>
的call
的返回值.
Thread.start() 和 Thread.run() 的区别
再来看一段代码:
- start:
运行结果:Client4Thread client4Thread = new Client4Thread(); client4Thread.start();
thread:Thread-0
- run:
运行结果:Client4Thread client4Thread = new Client4Thread(); client4Thread.run();
thread:main
结论:
如果调用Thread.run()
方法相当于一个普通类调用了一个run
方法,并没有开启子线程
相当于这样:
public static class User
public void run()
System.out.println("User.run" + Thread.currentThread().getName());
client4Thread.run()等价于User.run()!
Client4Thread client4Thread = new Client4Thread();
// client4Thread.start();
// 效果一样
client4Thread.run();
//
//
new User().run();
Thread.join() 线程串形化
先来看使用场景:
现在有三个线程,同时运行,先来运行看看结果:
可以看出,每次执行顺序都不同…
如果你想让他同步执行
,你可以这样做:
结论:
join的作用就是将线程串行化
,需要注意的是,join()
一定要放在现场开启(start()
)后,
Thread.Interrupt() 线程中断
运行代码:
运行结果为:
可以看出,虽然向子线程发送了中断信号,但是线程并不会执行终端,而是一直存活着.
如果想要线程中断,只需要在Thread
中判断一下isInterrupted()
即可
例如这样:
运行结果为:
小坑:
如果你需要在!isInterrupted()
中休眠一下,例如这样:
这样是不可以的,因为Thread.sleep
会捕获中断信号,并且将中断信号改为false
最终导致结果成这样了
最终就会导致捕获到了中断异常,但是Thread.sleep()
擅自更改了… 只需要在捕获到InterruptedException
异常的时候,再次更改状态即可:
例如这样:
当然不仅是extend Thread
这种写法可以中断线程,implements Runnable
当前也可以中断线程,比如这样写:
效果是一样的,就不重复展示了…
Thread 数据共享 synchronized(隐式锁)
还是先来看简单的案例:
现在有2
个线程,需要同时对count ++
,例如这样:
结果为:
可以看出,每次运行的结果都不一样?
这是为什么?
因为这里是循环添加 1000 * 100
次,在2个线程同时跑的时候,就会出现两个count
同时执行的情况,所以导致数字总是不太对.
这里只需要让他同步起来(添加同步锁)即可,例如这样:
常见锁的写法:
-
锁某一个类的class:
private static final Object object = new Object(); public static void addCount() for (int i = 0; i < countLength; i++) synchronized (object) count++;
-
锁当前对象的 this
public synchronized void addCount2() .....
-
锁指定对象的class:
public static void addCount() for (int i = 0; i < countLength; i++) synchronized (Client2.class) count++;
Thread线程隔离(ThreadLocal)
什么是线程隔离,还是先看代码:
还是先来看结果:
可以看出,每一次的运行效果都是不一样的
这是因为启动(start
)的时候,线程只是就绪状态
,只是通知JVM
我可以执行了,真正的执行是JVM
来调用的
以最后一次结果来看问题:
ThreadName:Thread-1 threadIndex:1 count:2
ThreadName:Thread-4 threadIndex:4 count:11
ThreadName:Thread-3 threadIndex:3 count:7
ThreadName:Thread-2 threadIndex:2 count:4
ThreadName:Thread-0 threadIndex:0 count:2
先来分析一下代码:
main
方法中,开启了5个线程,并且将下标传了进去,然后再MyThreadLocal
具体对下表累加
需求分析:
既然说到是线程隔离,那么想要的效果应该是假如现在传入进来的是5,那么通过
count+=threadIndex;
应该结果是6
,而不应该吧其他线程的结果也算到当前线程头上
这段话说的比较绕,直接来看ThreadLocal
就明白了!
启动的时候添加了一个Thread.join()
使线程顺序执行…
直接看效果:
可以看出,现在就实现了线程的隔离
如果你阅读过Handler
源码,也会发现他内部使用了ThreadLocal
来进行线程隔离
不过这里线程隔离隔离的是Lopper
,为了保证一个线程只有一个looper
瞟一眼handler
的代码:
notify(),notifyAll()和wait()的使用
tips:
notify() notifyAll() 和wait是Object()的属性,所以每一个类都可以调用
还是通过一段代码来看他们的区别:
-
WaitAndNotifyBean:
通过调用
waitName(),
来实现等待
通过changeName()
来实现刷新 -
MyThread:
-
使用:
来看看效果:
notify | notifyAll |
---|---|
可以看出,好像并没有什么区别,notify
和notifyAll
都能够唤醒wait
后的线程
接下来,加大剂量!
notify | notifyAll |
---|---|
可以看到,这下效果就很明显了!
多个线程wait
的情况下
notify
,只会刷新一个awit
,其他线程继续等待notifyAll
,会刷新所有的wait!
tips:
notifyAll notify() 和 wait()都需要放到锁
里面!
显示锁
可重入锁 (ReentrantLock)
什么是可重入?
int count = 0;
public synchronized void test()
count++;
if (count <= 10)
test();
先拿synchronized
来举例,以当前的认知来看,如果递归情况下,synchronized
一定会被锁死,因为第一次执行test()
的时候,还没有释放锁就进行了第二次的加锁
但是从结果来看,毫无问题,因为synchronized
是具有可重入性的,可重入就是指如果同一个实例的话,可重新进入
ReentrantLock 也是同样的道理
来看看ReentrantLock的使用:
结果还是那个结果:
这里需要注意的是,在使用隐式锁的时候,一定要放在try ... finally ..
代码块中
这样做是因为即使抛出异常,也会吧锁释放掉,不会出现死锁情况
规范示例:
lock.lock();
try
count++;
finally
lock.unlock();
读写锁 (ReentrantReadWriteLock)
要想突出ReentrantReadWriteLock的效果,那就必须有对比,这里就和synchronized来比较!
思路:
开启3个写线程,30个读线程,来模拟实际开发中的读取操作
通过同步锁(synchronized
)和读写锁的实际效果来看他们的运行机制!
在来看代码思路:
-
StudentImpl
-
getThread:
-
setThread:
运行:
synchronized效果和读写锁对比:
synchronized | 读写锁 |
---|---|
可以看出,效果是十分的明显,从变化中也可以看出,如果要进行大量的读写操作,使用读写锁效率会大大的提高!
课外知识 volatile!
volatile本不属于Thread的知识,但是既然说到这么多关键字,那就提一嘴吧!
还是一段代码引出区别:
未加volatile关键字:
效果图:
可以看出,不加volatile
关键字,isRun
,即使是改变了,线程中也是没反应,接受不到…
再来看看添加volatile
关键字:
效果图:
结果显而易见,一旦标记了volatile
isRun
变量只要发生改变就回直接通知JVM
来改变当前的状态!!
原创不易,您的点赞就是对我最大的支持!
以上是关于android thread知识你还记得多少?的主要内容,如果未能解决你的问题,请参考以下文章
Android面试题集:以前烂大街的四大组件-Activity,面试重提这些知识点你还记得吗?