Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx

上传人:b****2 文档编号:2116686 上传时间:2023-05-02 格式:DOCX 页数:21 大小:42.69KB
下载 相关 举报
Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx_第1页
第1页 / 共21页
Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx_第2页
第2页 / 共21页
Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx_第3页
第3页 / 共21页
Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx_第4页
第4页 / 共21页
Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx_第5页
第5页 / 共21页
Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx_第6页
第6页 / 共21页
Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx_第7页
第7页 / 共21页
Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx_第8页
第8页 / 共21页
Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx_第9页
第9页 / 共21页
Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx_第10页
第10页 / 共21页
Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx_第11页
第11页 / 共21页
Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx_第12页
第12页 / 共21页
Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx_第13页
第13页 / 共21页
Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx_第14页
第14页 / 共21页
Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx_第15页
第15页 / 共21页
Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx_第16页
第16页 / 共21页
Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx_第17页
第17页 / 共21页
Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx_第18页
第18页 / 共21页
Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx_第19页
第19页 / 共21页
Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx_第20页
第20页 / 共21页
亲,该文档总共21页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx

《Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx》由会员分享,可在线阅读,更多相关《Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx(21页珍藏版)》请在冰点文库上搜索。

Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁.docx

Android进阶多线程系列之waitnotifysleepjoinyieldsynchronized关键字ReentrantLock锁

Android进阶——多线程系列之wait、notify、sleep、join、yield、synchronized关键字、ReentrantLock锁

前言

多线程一直是初学者最困惑的地方,每次看到一篇文章,觉得很有难度,就马上叉掉,不看了,我以前也是这样过来的。

后来,我发现这样的态度不行,知难而退,永远进步不了。

于是,我狠下心来看完别人的博客,尽管很难但还是咬着牙,不懂去查阅资料,到最后弄懂整个过程。

虽然花费时间很大,但这就是自学的精髓,别人学不会,而我却学到了。

很简单的一个例子,一开始我对自定义View也是很抵触,看到很难的图就不去思考他,故意避开它,然而当我看到自己喜欢的雷达图时,很有兴趣的去查阅资料,不知不觉,自定义View对我已经没有难度了。

所以对于多线程我也是0基础,不过我还是咬着牙皮,该学的还是得学。

这里先总结这几个类特点和区别,让大家带着模糊印象来学习这篇文章

Thread是个线程,而且有自己的生命周期

对于线程常用的操作有:

wait(等待)、notify(唤醒)、notifyAll、sleep(睡眠)、join(阻塞)、yield(礼让)

wait、notify、notifyAll都必须在synchronized中执行,否则会抛出异常

synchronized关键字和ReentrantLock锁都是辅助线程同步使用的

初学者常犯的误区:

一个对象只有一个锁(正确的)

线程同步之synchronized关键字

马上就过年了,火车抢票又是一年沸沸扬扬的事情,这也就好比我们的多线程抢夺资源是一个道理,下面我们通过火车抢票的案例来理解

publicclassSyncActivityextendsAppCompatActivity{

privateintticket=10;

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_sync);

for(inti=0;i<10;i++){

newThread(){

@Override

publicvoidrun(){

//买票

sellTicket();

}

}.start();

}

}

publicvoidsellTicket(){

ticket--;

System.out.println("剩余的票数:

"+ticket);

}

}

这里我们通过开启十个线程来购买火车票,不过火车票只有十张,下面通过打印信息来看一下抢票的情况

剩余的票数:

9

剩余的票数:

8

剩余的票数:

7

剩余的票数:

6

剩余的票数:

5

剩余的票数:

1

剩余的票数:

1

剩余的票数:

1

剩余的票数:

1

剩余的票数:

0

可以发现,票数出现了误差,这明显就是不行的,这也是因为开启了十个线程,大家都抢着自己的票。

上面这种情况是因为其中有四个线程都挤在一起了,然后一起执行了【ticket–;】,接着再一起执行【System.out.println(“剩余的票数:

”+ticket);】导致的。

那么该如何保证大家都是能够自觉排队,井然有序的抢票呢。

这个时候就要用到synchronized关键字

方法一:

我们在方法上添加synchronized关键字

publicclassSyncActivityextendsAppCompatActivity{

privateintticket=10;

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_sync);

for(inti=0;i<10;i++){

newThread(){

@Override

publicvoidrun(){

//买票

sellTicket();

}

}.start();

}

}

//添加在这里

publicsynchronizedvoidsellTicket(){

ticket--;

System.out.println("剩余的票数:

"+ticket);

}

}

这样就表示这个方法是同步的,只能由一个个线程来争夺里面的资源,下面通过打印信息可以验证

剩余的票数:

9

剩余的票数:

8

剩余的票数:

7

剩余的票数:

6

剩余的票数:

5

剩余的票数:

4

剩余的票数:

3

剩余的票数:

2

剩余的票数:

1

剩余的票数:

0

方法二:

我们在方法内添加synchronized关键字

publicclassSyncActivityextendsAppCompatActivity{

privateintticket=10;

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_sync);

for(inti=0;i<10;i++){

newThread(){

@Override

publicvoidrun(){

//买票

sellTicket();

}

}.start();

}

}

//添加在这里

Objectlock=newObject();

publicvoidsellTicket(){

synchronized(lock){

ticket--;

System.out.println("剩余的票数:

"+ticket);

}

}

}

其实,synchronized关键字可以理解为一个锁,而锁就需要被锁的东西,所以synchronized又分为类锁和对象锁,即可以锁类又可以锁对象,它们共同的作用就是保证线程的同步。

就好比如我们上面中synchronized(lock),就是对象锁,将Object对象锁起来

一、类锁和对象锁的概念

对象锁和类锁在锁的概念上基本上和内置锁是一致的,但是在多线程访问时,两个锁实际是有很大的区别的,对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。

我们知道,类的对象实例可以有很多个,但是每个类只有一个class对象,所以,结论是:

1、不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。

2、而且类锁和对象锁互相不干扰。

二、对象锁

类锁创建如下两种方法

publicclassSynchronizedDemo{

//同步方法,对象锁

publicsynchronizedvoidsyncMethod(){

}

//同步块,对象锁

publicvoidsyncThis(){

synchronized(this){

}

}

}

三、类锁

对象锁创建如下两种方法

publicclassSynchronizedDemo{

//同步class对象,类锁

publicvoidsyncClassMethod(){

synchronized(SynchronizedDemo.class){

}

}

//同步静态方法,类锁

publicstaticsynchronizedvoidsyncStaticMethod(){

}

}

四、通过例子理解结论和概念

根据类锁和对象锁的概念,我们来通过例子验证一下其正确性,这里演示两个对象锁和一个类锁,我们创建一个类

publicclassSynchronizedDemo{

privateintticket=10;

//同步方法,对象锁

publicsynchronizedvoidsyncMethod(){

for(inti=0;i<1000;i++){

ticket--;

System.out.println(Thread.currentThread().getName()+"剩余的票数:

"+ticket);

}

}

//同步块,对象锁

publicvoidsyncThis(){

synchronized(this){

for(inti=0;i<1000;i++){

ticket--;

System.out.println(Thread.currentThread().getName()+"剩余的票数:

"+ticket);

}

}

}

//同步class对象,类锁

publicvoidsyncClassMethod(){

synchronized(SynchronizedDemo.class){

for(inti=0;i<50;i++){

ticket--;

System.out.println(Thread.currentThread().getName()+"剩余的票数:

"+ticket);

}

}

}

}

情况一:

同一个对象,使用两个线程调用不同对象锁

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_sync);

finalSynchronizedDemosynchronizedDemo=newSynchronizedDemo();

//线程一

newThread(){

@Override

publicvoidrun(){

synchronizedDemo.syncMethod();

}

}.start();

//线程二

newThread(){

@Override

publicvoidrun(){

synchronizedDemo.syncClassMethod();

}

}.start();

}

由于使用的是同一个对象的对象锁,所以执行出来的结果是同步的(即先运行线程一,等线程一运行完后运行线程二,ticket有序的减少),这里使用1000比较大的数字是为了一次能看出效果

Thread-1611剩余的票数:

7

Thread-1611剩余的票数:

6

Thread-1611剩余的票数:

5

Thread-1611剩余的票数:

4

Thread-1611剩余的票数:

3

Thread-1611剩余的票数:

2

情况二:

不同对象,使用两个线程调用同个对象锁

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_sync);

finalSynchronizedDemosynchronizedDemo1=newSynchronizedDemo();

finalSynchronizedDemosynchronizedDemo2=newSynchronizedDemo();

//线程一

newThread(){

@Override

publicvoidrun(){

synchronizedDemo1.syncMethod();

}

}.start();

//线程二

newThread(){

@Override

publicvoidrun(){

synchronizedDemo2.syncMethod();

}

}.start();

}

由于是不同对象,所以执行的对象锁都不是不同的,其结果是两个线程互相抢占资源的运行,即ticket偶尔会无序的减少

Thread-1667剩余的票数:

-1612

Thread-1667剩余的票数:

-1613

Thread-1668剩余的票数:

-1630

Thread-1668剩余的票数:

-1631

Thread-1668剩余的票数:

-1632

情况三:

同一个对象,使用两个线程调用一个对象锁一个类锁

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_sync);

finalSynchronizedDemosynchronizedDemo=newSynchronizedDemo();

//线程一

newThread(){

@Override

publicvoidrun(){

synchronizedDemo.syncMethod();

}

}.start();

//线程二

newThread(){

@Override

publicvoidrun(){

synchronizedDemo.syncClassMethod();

}

}.start();

}

由于对象锁和类锁互不干扰,所以也是线程不安全的

Thread-1667剩余的票数:

-1612

Thread-1667剩余的票数:

-1613

Thread-1668剩余的票数:

-1630

Thread-1668剩余的票数:

-1631

Thread-1668剩余的票数:

-1632

这里再温习一下结论:

1、不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。

2、而且类锁和对象锁互相不干扰。

不知不觉synchronized介绍了那么多,本可以放单独一篇文章的,不过后面的不多,认真看的人应该有点收获

线程同步之ReentrantLock锁

Java6.0增加了一种新的机制:

ReentrantLock。

ReentrantLock比synchronized理解简单多了,下面看ReentrantLock的使用

publicclassRenntrantLockActivityextendsAppCompatActivity{

Locklock;

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_renntrant_lock);

lock=newReentrantLock();

doSth();

}

publicvoiddoSth(){

lock.lock();

try{

//这里执行线程同步操作

}finally{

lock.unlock();

}

}

}

使用ReentrantLock很好理解,就好比我们现实的锁头是一样道理的。

使用ReentrantLock的一般组合是lock与unlock成对出现的,需要注意的是,千万不要忘记调用unlock来释放锁,否则可能会引发死锁等问题。

如果忘记了在finally块中释放锁,可能会在程序中留下一个定时炸弹,随时都会炸了,而是用synchronized,JVM将确保锁会获得自动释放,这也是为什么Lock没有完全替代掉synchronized的原因

线程的生命周期的介绍

线程也有属于自己的生命周期,这里使用我画的一张图来理解,在下面我们会讲解这个有关生命周期的一些方法的使用

线程的等待唤醒机制之wait()、notify()、notifyAll()

一开始我们也提到了wait、notify、notifyAll都必须在synchronized中执行,否则会抛出异常。

所以下面以一个简单的例子来介绍线程的等待唤醒机制

publicclassWaitAndNotifyActivityextendsAppCompatActivity{

privatestaticObjectlockObject=newObject();

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_wait_and_notify);

System.out.println("主线程运行");

//创建子线程

Threadthread=newWaitThread();

thread.start();

longstart=System.currentTimeMillis();

synchronized(lockObject){

try{

System.out.println("主线程等待");

lockObject.wait();

}catch(InterruptedExceptione){

e.printStackTrace();

}

System.out.println("主线程继续-->等待的时间:

"+(System.currentTimeMillis()-start));

}

}

classWaitThreadextendsThread{

@O

publicvoidrun(){

synchronized(lockObject){

try{

//子线程等待了2秒钟后唤醒lockObject锁

Thread.sleep(2000);

lockObject.notifyAll();

}catch(InterruptedExceptione){

e.printStackTrace();

}

}

}

}

}

可以看到,我们使用的是同一个对象的锁,和同一个对象执行的wait()和notify()才会保证了我们的线程同步。

当主线程执行到wait()方法时,代表主线程等待,让出使用权让子线程执行,这个时候主线程等待这一事件会被加进到【等待唤醒的队列】中。

然后子线程则是两秒钟后执行notify()方法唤醒等待【唤醒队列中】的第一个线程,这里指的是主线程。

而notifyAll()方法则是唤醒整个【唤醒队列中】的所有线程,这里就不多加演示了

下面采用一道经典的Java多线程面试题来让大家练习熟悉熟悉:

子线程循环10次,接着主线程循环15次,接着又回到子线程循环10次,接着再回到主线程又循环15次,如此循环50次

//子线程

newThread(){

@Override

publicvoidrun(){

for(inti=0;i<50;i++){

for(intj=0;j<10;j++){

System.out.println("子循环循环第"+(j+1)+"次");

}

System.out.println("-->子线程循环了"+(i+1)+"次");

}

}

}.start();

//主线程

for(inti=0;i<50;i++){

for(intj=0;j<15;j++){

System.out.println("主循环循环第"+(j+1)+"次");

}

System.out.println("-->主线程循环了"+(i+1)+"次");

}

首先是主要思路的搭建,现在的问题就是如何让子线程和主线程有序的执行呢,那肯定是我们的等待唤醒机制

//子线程

newTread(){

@Override

publicvoidrun(){

for(inti=0;i<50;i++){

synchronized(lock){

for(intj=0;j<10;j++){

System.out.println("子循环循环第"+(j+1)+"次");

}

//唤醒

lock.notify();

//等待

try{

lock.wait();

}catch(InterruptedExceptione){

e.printStackTrace();

}

}

}

}

}.start();

//主线程

for(inti=0;i<50;i++){

synchronized(lock){

//等待

try{

lock.wait();

}catch(InterruptedExceptione){

StackTrace();

}

for(intj=0;j<15;j++){

System.out.println("主循环循环第"+(j+1)+"次");

}

//唤醒

lock.notify();

}

}

不管是主线程先运行还是子线程运行,两个线程只能同时进入synchronized(lock)一个锁中。

由于是子线程先运行:

1、当主线程先进入synchronized(lock)锁时,它就必须是等待,而子线程开始运行输出,输出后就唤醒主线程。

2、当子线程先运行的话,那就直接输出,然后等待主线程的运行输出

线程的sleep()、join()、yield()

一、sleep()

sleep()作用是让线程休息指定的时间,时间一到就继续运行,它的使用很简单

try{

Thread.sleep(2000);

}catch(InterruptedExceptione){

e.printStackTrace();

}

二、join()

join()作用是让指定的线程先执行完再执行其他线程,而且会阻塞主线程,它的使用也很简单

publicclassJoinActivityextendsAppCompatAc

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 小学教育 > 语文

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2