多线程一直是初学者最困惑的地方,每次看到一篇文章,觉得很有难度,就马上叉掉,不看了,我以前也是这样过来的。后来,我发现这样的态度不行,知难而退,永远进步不了。于是,我狠下心来看完别人的博客,尽管很难但还是咬着牙,不懂去查阅资料,到最后弄懂整个过程。虽然花费时间很大,但这就是自学的精髓,别人学不会,而我却学到了。很简单的一个例子,一开始我对自定义View也是很抵触,看到很难的图就不去思考他,故意避开它,然而当我看到自己喜欢的雷达图时,很有兴趣的去查阅资料,不知不觉,自定义View对我已经没有难度了。所以对于多线程我也是0基础,不过我还是咬着牙皮,该学的还是得学。这里先总结这几个类特点和区别,让大家带着模糊印象来学习这篇文章

  1. Thread是个线程,而且有自己的生命周期
  2. 对于线程常用的操作有:wait(等待)、notify(唤醒)、notifyAll、sleep(睡眠)、join(阻塞)、yield(礼让)
  3. wait、notify、notifyAll都必须在synchronized中执行,否则会抛出异常
  4. synchronized关键字和ReentrantLock锁都是辅助线程同步使用的
  5. 初学者常犯的误区:一个对象只有一个锁(正确的)

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

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

可以发现,票数出现了误差,这明显就是不行的,这也是因为开启了十个线程,大家都抢着自己的票。上面这种情况是因为其中有四个线程都挤在一起了,然后一起执行了【ticket–;】,接着再一起执行【System.out.println(“剩余的票数:” + ticket);】导致的。那么该如何保证大家都是能够自觉排队,井然有序的抢票呢。这个时候就要用到synchronized关键字

一、方法上添加synchronized关键字

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

二、方法内添加synchronized关键字

其实,synchronized关键字可以理解为一个锁,而锁就需要被锁的东西,所以synchronized又分为类锁和对象锁,即可以锁类又可以锁对象,它们共同的作用就是保证线程的同步。就好比如我们上面中synchronized(lock),就是对象锁,将Object对象锁起来

对象锁和类锁在锁的概念上基本上和内置锁是一致的,但是在多线程访问时,两个锁实际是有很大的区别的,对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。我们知道,类的对象实例可以有很多个,但是每个类只有一个class对象,所以,结论是:1、不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。2、而且类锁和对象锁互相不干扰。

一、对象锁

类锁创建如下两种方法

二、类锁

对象锁创建如下两种方法

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

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

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

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

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

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

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

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

温习结论:1、不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。2、而且类锁和对象锁互相不干扰。

Java6.0增加了一种新的机制:ReentrantLock,下面看ReentrantLock的使用

使用ReentrantLock很好理解,就好比我们现实的锁头是一样道理的。使用ReentrantLock的一般组合是lock与unlock成对出现的,需要注意的是,千万不要忘记调用unlock来释放锁,否则可能会引发死锁等问题。如果忘记了在finally块中释放锁,可能会在程序中留下一个定时炸弹,随时都会炸了,而是用synchronized,JVM将确保锁会获得自动释放,这也是为什么Lock没有完全替代掉synchronized的原因

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

一开始我们也提到了wait、notify、notifyAll都必须在synchronized中执行,否则会抛出异常。所以下面以一个简单的例子来介绍线程的等待唤醒机制

可以看到,我们使用的是同一个对象的锁,和同一个对象执行的wait()和notify()才会保证了我们的线程同步。当主线程执行到wait()方法时,代表主线程等待,让出使用权让子线程执行,这个时候主线程等待这一事件会被加进到【等待唤醒的队列】中。然后子线程则是两秒钟后执行notify()方法唤醒等待【唤醒队列中】的第一个线程,这里指的是主线程。而notifyAll()方法则是唤醒整个【唤醒队列中】的所有线程,这里就不多加演示了

下面采用一道经典的Java多线程面试题来让大家练习熟悉熟悉:子线程循环10次,接着主线程循环15次,接着又回到子线程循环10次,接着再回到主线程又循环15次,如此循环50次

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

不管是主线程先运行还是子线程运行,两个线程只能同时进入synchronized (lock)一个锁中。由于是子线程先运行:1、当主线程先进入synchronized (lock)锁时,它就必须是等待,而子线程开始运行输出,输出后就唤醒主线程。2、当子线程先运行的话,那就直接输出,然后等待主线程的运行输出

一、sleep()

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

二、join()

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

这里就不解释了,看打印信息,你就能发现它的作用了

三、yield()

yield()的作用是指定线程先礼让一下别的线程的先执行,就好比公交车只有一个座位,雅思高频词汇谁礼让了谁就坐上去。特别注意的是:yield()会礼让给相同优先级的或者是优先级更高的线程执行,不过yield()这个方法只是把线程的执行状态打回准备就绪状态,所以执行了该方法后,有可能马上又开始运行,有可能等待很长时间

这里我们通过Thread.sleep()的方式,让线程强行延迟一秒回到准备就绪状态,这样在打印信息上就能看到我们想要的结果了

好了,关于线程的介绍就这么多,可能知识点有点多,我自己也学习了好几天来掌握线程,这里的分享我都是测试过的。学习一遍才知道原来是这么一回事,没学习之前看别人的文章还是懂的,当自己码一遍的时候会发现写不出来,原因是没有真正理解线程。现在理解了线程之后,写出来会根据它的作用和思路来写,根本不用记代码

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

  1. Android进阶——多线程系列之Thread、Runnable、Callable、Future、FutureTask

    多线程一直是初学者最抵触的东西,如果你想进阶的话,那必须闯过这道难关,特别是多线程中Thread.Runnable.Callable.Future.FutureTask这几个类往往是初学者容易搞混的. ...

  2. java 多线程系列基础篇(四)之 synchronized关键字

    1. synchronized原理 在java中,每一个对象有且仅有一个同步锁.这也意味着,同步锁是依赖于对象而存在.当我们调用某对象的synchronized方法时,就获取了该对象的同步锁.例如,s ...

  3. Android进阶——多线程系列之异步任务AsyncTask的使用与源码分析

    AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并主线程中更新UI,通过AsyncTask可以更加方便执行后台任务以及在主线程中访问UI ...

  4. Android进阶——多线程系列之Semaphore、CyclicBarrier、CountDownLatch

    今天向大家介绍的是多线程开发中的一些辅助类,他们的作用无非就是帮助我们让多个线程按照我们想要的执行顺序来执行.如果我们按照文字来理解Semaphore.CyclicBarrier.CountDownL ...

  5. Android进阶——多线程系列之四大线程池的使用介绍

    线程池一直是初学者最抵触的东西,由于刚开始学习做项目并不会涉及到线程池的使用,但是不去学习它,心里又好像有个石头一直沉着,一直放心不下,其实是很简单的东西,早晚都要学,不如趁现在吧.由于文章从初学者的 ...

  6. java高并发系列 - 第10天:线程安全和synchronized关键字

    这是并发系列第10篇文章. 什么是线程安全? 当多个线程去访问同一个类(对象或方法)的时候,该类都能表现出正常的行为(与自己预想的结果一致),那我们就可以所这个类是线程安全的. 看一段代码: pack ...

  7. Java多线程系列--“基础篇”08之 join()

    概要 本章,会对Thread中join()方法进行介绍.涉及到的内容包括:1. join()介绍2. join()源码分析(基于JDK1.7.0_40)3. join()示例 转载请注明出处:http ...

  8. 【转】wait,notify,notifyAll,join,yield,sleep的区别和联系

    1.  Thread.sleep(long) 和Thread.yield()都是Thread类的静态方法,在调用的时候都是Thread.sleep(long)/Thread.yield()的方式进行调 ...

  9. [转]wait,notify,notifyAll,join,yield,sleep的区别和联系

    1.  Thread.sleep(long) 和Thread.yield()都是Thread类的静态方法,在调用的时候都是Thread.sleep(long)/Thread.yield()的方式进行调 ...

随机推荐

  1. Ubuntu 16.04.4下安装apache服务

     Ubuntu 16.04.4下安装apache服务: 一.首先,准备需要的预装环境 需要c++,make,gcc,apr  apr-util  pcre.(如果后面报错缺少什么组件,可以百度搜方法. ...

  2. C语言三种整数类型

    1,int 是 C 语言的基本整数类型,可以满足我们处理一般数据的需求. C 语言还提供了四个可以修饰 int 的关键字:short.long.signed,以及 unsigned. 利用这四个关键字 ...

  3. WAFの基本防护透明流模式v1.0

                      一.WAFの透明流模式     1)首先先配置WAF的网络,配置一个网桥接口,设置IP便于带内管理.             2)当然,如果需要不同网段之间都能够管 ...

  4. H7-TOOL脱机烧录器功能开源发布

    H7-TOOL汇总帖:https://www.cnblogs.com/armfly/p/12283459.html 当前已经对STM32F030,STM32F103,STM32F429,STM32F7 ...

  5. python基础学习(一)

    一,Python介绍 1,python的出生与应用 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆(中文名字:龟叔)为了在阿姆斯特丹打 ...

  6. 04.Delphi通过接口IInterface实现多重继承

    IInterface表示申明了一些函数,自己本身没有实现部分,需要由继承它的类来实现函数 uSayHello代码如下 unit uSayHello; interface uses SysUtils, ...

  7. RDD 可视化 —— RDDOperationScope.withScope

    最近在看各种博客,学习 spark 源代码. 网上对源代码的分析基本都是基于 0.7, 0.8, 1.0 的代码,而现在的发行版已经是 1.5 了.所以有些代码不大对的上.比如函数 RDD.map() ...

  8. 《ES6标准入门》(阮一峰)--5.字符串的新增方法

    1.String.fromCodePoint() ES5 提供String.fromCharCode()方法,用于从 Unicode 码点返回对应字符,但是这个方法不能识别码点大于0xFFFF的字符. ...

  9. Codeforces Round #622 (Div. 2) 题解和我的分析

    首先下午场非常适合中国人,开心 A 三种食物有个数限制,上菜,每次上菜跟以前的样式不能一样(食物的种类及个数各不相同),且每种食物最多用一次,问最多能上几次 对a,b,c排序,然后枚举上菜种类就可以了 ...

  10. HIVE ROW_NUMBER()函数去重

    SELECT * FROM( SELECT *,ROW_NUMBER() OVER(PARTITION BY a.claimno ORDER BY b.financiancedate DESC)  n ...