第2章 对象及变量的并发访问

2.1 synchronized同步方法

方法内的变量为线程安全:

  方法内部的变量是线程私有的

  方法中有一个变量num,后面对它赋值

  两个线程同时调用这个方法,对其赋不同的值,不会出现非线程安全的问题

实例变量非线程安全:

  多个线程共同访问一个对象中的实例变量,则会出现非线程安全的问题

  对方法加上synchrongized,则可以解决该问题

多个对象多个锁:

  两个线程访问不同对象的方法,则是线程安全的

  即使获得了锁,也不会同步,因为不是用一个对象的锁

  A线程先持有object对象的Lock锁,B线程可以以异步的方式调用object对象中的非synchronized类型的方法。

  A线程先持有object对象的Lock锁,B线程如果在这时调用object对象中的synchronized类型的方法则需等待,也就是同步。

synchronized锁重入

  关键字synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的。

  这也证明在一个synchronized方法/块的内部调用本类的其他synchronized方法/块时,是永远可以得到锁的。

  可重入锁也支持在父子类继承的环境中。

出现异常,锁自动释放

同步不具有继承性

2.2 synchronized同步语句块

  synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个长时间的任务,那么B线程则必须等待较长的时间,在这种情况下可以使用同步语句块来解决。

  当一个线程访问object的一个synchronized同步代码块时,另一个线程仍然可以访问该object对象中的非synchronized(this)同步代码块。

  当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被堵塞,这说明synchronized使用的“对象监视器”是一个。

  如果使用同步代码块锁定非this对象,则不会与其他硕this同步方法争抢this锁。

  synchronized加到静态方法上,是给Class类加锁,加到非静态方法上,是给对象加锁

  程序设计时要避免双方互相持有对方的锁的情况,避免死锁。

2.3 volatile关键字

  一个线程执行while(flag)的无限循环,主线程再将flag置为false,还是有可能出现死循环,解决办法是用volatile

  在JVM -server模式中,为了线程运行的效率,线程一直在私有堆栈中获取flag的值为true,主线程修改的是公共堆栈中的flag

  使用volatile关键字,强制从公共内存中读取变量的值 

关键字synchronized和volatile进行一下比较:

  关键字volatile只能修饰变量,而synchronized可以修饰方法,以及代码块,随着JDK新版本的发布,synchronized关键字在执行效率上得到很大的提升,在开发中时用synchronized关键字的比率还是比较大的。

  多线程访问volatile不会发生堵塞,而synchronized会出现堵塞。

  volatile能保证数据的可见性,但不能保证原子性,而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步。

  关键字volatile解决的是变量在多个线程之间的可见性,而synchronized关键字解决的是多个线程之间访问资源的同步性。

使用原子类进行i++ :

  除了在i++操作是使用synchronized关键字实现同步外,还可以使用AtomicInteger原子类进行实现。

  原子操作是不能分割的整体,没有其他线程能够中断或检查正在原子操作中的变量。一个原子类型就是一个原子操作可用的类型,它可以在没有锁的情况下做到线程安全。

  关键字synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法或某一个代码块。

  它包含两个特征:互斥性和可以见性。同步synchronized不仅可以解决一个线程看到的对象处于不一致的状态,还可以保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护之前所有的修改效果。

第3章 线程间通信

3.1 等待/通知机制

wait():

  在调用wait()方法之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法。

  wait()方法是Object类的方法,该方法用来将当前线程置入“预执行队列”中,并且在wait()所在的代码行处停止执行,直到接到通知或被中断为止。

  在执行wait()方法后,当前线程释放锁。(sleep不会释放锁。)

notify():

  方法notify()也要在同步方法或同步块中调用,即在调用前,线程也必须获得该对象的对象级别锁

  该方法用来通知那些可能等待该对象锁的其他线程,如果有多个线程等待,则由线程规划器随机挑选出其中一个呈wait状态的线程,对其发出通知notify,并使它等待获取该对象的对象锁。

  在执行notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出synchronized代码块后,当前线程才会释放锁,而呈wait状态所在的线程才可以获取该对象锁。

总结:

  wait 使线程停止运行,notify 使停止的线程继续运行。

  wait ()方法可以使调用该方法的线程释放共享资源的锁,从运行状态退出,进入等待状态,直到再次被唤醒。

  notify() 方法可以随机唤醒等待队列中等待同一共享资源一个线程,并使该线程退出等待状态,进入可运行状态

  notifyAll() 方法可以随机唤醒等待队列中等待同一共享资源的所有线程,并使这些线程退出等待状态,进入可运行状态。

线程状态:

  新创建一个线程对象后,调用它的 start() 方法,系统会为此线程分配 CPU 资源,使其处于 Runnable(可运行)状态,如果线程抢占到 CPU 资源,此线程就会处于 Running (运行)状态

  Runnable 和 Running 状态之间可以相互切换,因为线程有可能运行一段时间后,有其他优先级高的线程抢占了 CPU 资源,此时线程就从 Running 状态变成了 Runnable 状态。

  线程wait()之后,调用线程对象的interrupt()方法会出现异常。

  wait(long):等待某一时间内是否有线程对锁进行唤醒,超过这个时间则自动唤醒。

生产者消费者问题:

  用wait和notify的话,注意使用循环和notifyAll

通过管道进行线程间通信:

  字节流:PipedInputStream,PipedOutputStream

  字符流:PipedReader,PipedWriter

3.2 join

  使所属的线程对象x正常执行run(),当前线程z无限期阻塞,等待线程x被销毁后再执行线程z。

  join 方法具有使线程排队运行的作用,有些类似同步的运行效果。

  join 与 synchronized 的区别是:join 在内部使用 wait() 方法进行等待,而 synchronized 关键字使用的是 “对象监视器” 原理做为同步。

  在 join 过程中,如果当前线程对象被中断,则当前线程出现异常。

  方法 join(long) 中的参数是设定等待的时间。

  join(long)在内部是使用wait(long)实现的,所以这个方法会释放锁,而sleep(long)不会释放锁。

  t.join()方法阻塞调用此方法的线程(calling thread),直到线程t完成,此线程再继续;

  通常用于在main()主线程内,等待其它线程完成再结束main()主线程。例如:

 public class JoinTester01 implements Runnable {

     private String name;

     public JoinTester01(String name) {
this.name = name;
} public void run() {
System.out.printf("%s begins: %s\n", name, new Date());
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("%s has finished: %s\n", name, new Date());
} public static void main(String[] args) {
Thread thread1 = new Thread(new JoinTester01("One"));
Thread thread2 = new Thread(new JoinTester01("Two"));
thread1.start();
thread2.start(); try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} System.out.println("Main thread is finished");
} }

3.3 ThreadLocal

  解决的是变量在不同线程间的隔离性,也就是不同线程拥有自己的值。

3.4 InheritableThreadLocal

  可以在子线程中取得父线程继承下来的值。

第4章 Lock 的使用

4.1 使用 ReentrantLock 类

使用下面代码获取ReenTrantLock对象lock:

  private Lock lock = new ReentrantLock();

  调用lock.lock()方法可以对代码进行加锁,调用lock.unlock()方法对代码进行解锁。

  调用了lock()方法代码的线程会持有“对象监视器”,其他线程只有等待锁被释放时再次争抢,效果跟使用synchronized关键字一样,线程间还是顺序执行的。

Condition:

  类ReentrantLock实现等待/同步功能,需要借助于condition对象。

  Condition类具有很好的灵活性,可以实现多路通知功能:在一个Lock对象中创建多个Condition(对象监视器)实例,线程对象可以注册在指定的Condition中,从而有选择性地进行线程通知,在调度线程上更灵活。

  在使用notify()/notifyAll()方法进行通知时,被通知的线程是由JVM随机选择的,但使用ReentrantLock结合Condition类是可以实现前面介绍过的“选择性通知”,这个功能是常用,且在Condition中是默认的。

  synchronized相当于整个Lock对象中只有一个单一的condition对象,所有线程都注册在他一个对象上。

  Object类中的wait()方法相当于Condition类中的await()方法。

  Object类中的wait(long timeout)方法相当于Condition类中的await(long time, TimeUnit unit)方法。

  Object类中的notify()方法相当于Condition类中的single()方法。

  Object类中的notifyAll()方法相当于Condition类中的singleAll()方法。

公平锁与非公平锁:

  公平锁:标识线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序。

  非公平锁(默认):一种线程抢占机制,是随机获取锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方法可能会导致某些线程一直拿不到锁,自然不公平。

  创建的方式就是:

  private Lock lock = new ReentrantLock(Boolean flag);

  flag为true就是公平锁,flag为false就是非公平锁。

ReentrantLock类的API:

  int getHoldCount() :查询当前线程保持此锁定的个数,也就是调用lock()方法的次数。

  int getQueueLength():返回正等待获取此锁定线程估计数。

  int getWaitQueueLength(Condition condition):返回等待与此锁定相关的给定条件Condition的线程估计数。

      比如有5个线程,每个线程都执行了同一个condition对象的await()方法,则调用getWaitQueueLength()返回的值就是5。

  boolean hasQueuedThread(Thread thread):查询指定的线程是否正在等待获取此锁定。  

  boolean hasQueuedThreads():查询是否有线程正在等待获取此锁定。

  boolean hasWaiters(Condition condition):查询是否有线程正在等待与此锁定有关的condition条件。

  boolean isFair():判断是不是公平锁

  boolean isHeldByCurrentThread():查询当前线程是否保持此锁定

  boolean isLocked():查询此锁定是否由任意线程保持

  void lockInterruptibly():如果当前线程未被中断(不处于中断状态),则获得锁定,如果已经被检测中断(处于中断状态)则出现异常。

  boolean tryLcok():仅在调用时锁定未被另一个线程保持的情况下,才获得该锁定。

  boolean tryLock(long timeout, TimeUnit unit):如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获得该锁定。

condition的API:

  awaitUninterruptibly():此方法为condition的方法await()的替代。可以避免在挂起的时候遇到打断状态(interrupt)而爆出异常。

  awaitUntil():在指定时间后唤醒自己。参数为long。在线程等待期间也可以被其他线程唤醒。

  

4.2 使用 ReentrantReadWriteLock 类

  读写锁:一个是读操作相关的锁,也成为共享锁;另一个是写操作相关的锁,也叫排他锁。

  读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥。

  在没有线程进行写操作时,多个进行读操作的线程都可以获取读锁,而进行写入操作的Thread只有在获取写锁后才能进行写入操作。

  多个线程可以同时进行读操作,但同一时刻只允许一个Thread进行写入操作。 

可以通过如下代码获取读写锁:

  private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

  lock.readLock().lock():可以获得读锁,并进行加锁。

  lock.writeLock().lock():就可以获得写锁,并进行加锁。

《Java多线程编程核心技术》学习笔记的更多相关文章

  1. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  2. PHP-自定义模板-学习笔记

    1.  开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2.  整体架构图 ...

  3. PHP-会员登录与注册例子解析-学习笔记

    1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...

  4. 2014年暑假c#学习笔记目录

    2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...

  5. JAVA GUI编程学习笔记目录

    2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...

  6. seaJs学习笔记2 – seaJs组建库的使用

    原文地址:seaJs学习笔记2 – seaJs组建库的使用 我觉得学习新东西并不是会使用它就够了的,会使用仅仅代表你看懂了,理解了,二不代表你深入了,彻悟了它的精髓. 所以不断的学习将是源源不断. 最 ...

  7. CSS学习笔记

    CSS学习笔记 2016年12月15日整理 CSS基础 Chapter1 在console输入escape("宋体") ENTER 就会出现unicode编码 显示"%u ...

  8. HTML学习笔记

    HTML学习笔记 2016年12月15日整理 Chapter1 URL(scheme://host.domain:port/path/filename) scheme: 定义因特网服务的类型,常见的为 ...

  9. DirectX Graphics Infrastructure(DXGI):最佳范例 学习笔记

    今天要学习的这篇文章写的算是比较早的了,大概在DX11时代就写好了,当时龙书11版看得很潦草,并没有注意这篇文章,现在看12,觉得是跳不过去的一篇文章,地址如下: https://msdn.micro ...

  10. ucos实时操作系统学习笔记——任务间通信(消息)

    ucos另一种任务间通信的机制是消息(mbox),个人感觉是它是queue中只有一个信息的特殊情况,从代码中可以很清楚的看到,因为之前有关于queue的学习笔记,所以一并讲一下mbox.为什么有了qu ...

随机推荐

  1. linux下彻底卸载mysql 图解教程

    linux下彻底卸载mysql 图解教程 1.查找以前是否装有mysql 命令:rpm -qa|grep -i mysql可以看到如下图的所示: 说明之前安装了:MySQL-client-5.5.25 ...

  2. 51Nod 1384 全排列

    给出一个字符串S(可能有重复的字符),按照字典序从小到大,输出S包括的字符组成的所有排列.例如:S = "1312", 输出为:   1123 1132 1213 1231 131 ...

  3. 【转】如何快速识别应用MOS管,几张图片就搞定了

    三极管是流控型器件,MOS管是压控型器件,两者存在相似之处.三极管机可能经常用,但MOS管你用的可能较少.对于MOS管先抛出几个问题: 如何区分P-MOS和N-MOS: 如何区分MOS的G.D.S管脚 ...

  4. Python GIL全局解释器锁

    '''在python原始解释器Cpython中存在GIL(Global Interpreter Lock,全局解释器锁),因此在执行Python代码 时,会产生互斥锁来限制线程对共享资源的访问,指导接 ...

  5. angular的一次小错误

    前台页面的错误: 在使用angular的时候,发现了标签等不能解析,忙了一个小时没找见错误在哪,最后才发现,原来ng-app,ng-controller等声明错了地方,声明在了div上,而不是在bod ...

  6. 函数和常用模块【day06】:time模块 (一)

    本节内容 1.简述 2.time模块 3.时间格式转换 一.简述 我们在写代码的过程经常遇到时间模块,如果我们以后需要根据时间去筛选信息的话,那用户会更大,所以今天就来讲讲时间的两大模块:time & ...

  7. RESTful记录-RESTful服务

    按照REST架构,一个RESTful Web服务不应该继续服务器的客户端的状态.这种限制被称为无状态.它负责客户以它的上下文传递给服务器,然后服务器可以存储这样的上下文,以处理客户端的进一步请求.例如 ...

  8. bzoj千题计划294:bzoj3139: [Hnoi2013]比赛

    http://www.lydsy.com/JudgeOnline/problem.php?id=3139 队伍的顺序不会影响结果 将队伍的得分情况作为状态,记忆化搜索 就是先搜索第一只队伍的得分情况, ...

  9. bzoj千题计划238:bzoj3668: [Noi2014]起床困难综合症

    http://www.lydsy.com/JudgeOnline/problem.php?id=3668 这..一位一位的来就好了呀 #include<cstdio> #include&l ...

  10. 2018年10月14日ICPC南京站参赛总结

    这次比赛消耗掉了我们全部的信心和精力 在热身赛上,总体来说还是比较愉快的,这个时候心态就不对 正赛的时候我们遇到了A题签到题 我一开始是读错了题意了,认为这个题是一个裸的SG函数,而且那么多人秒过 W ...