最近在研究多线程同步的一些问题,整理了网上很多文档,在这里给大家分享下

Java并发机制的底层实现原理

Java代码在编译后会变成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码,最终需要转化为汇编指令在CPU上执行,Java中所使用的并发机制依赖于JVM的实现和CPU的指令。

多线程并发时,多个线程同时请求同一个资源,必然导致此资源的数据不安全,A线程修改了B线程的处理的数据,而B线程又修改了A线程处理的数理。为了解决此问题,就必须使用同步的方式保证程序不会产生设计之外的错误结果。

下面简单说下Java同步机制的4种实现方式: 目的都是为了解决多线程中的对同一变量的访问冲突

使用wait()和notify()

wait():使一个线程处于等待状态,并且释放所持有的对象的 lock。

sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉

InterruptedException(中断异常)异常。

notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒

某一个等待状态的线程,而是由 JVM 确定唤醒哪个线程,而且不是按优先级。

notityAll():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是

让它们竞争。

synchronized实现同步的方式

(1)同步方法:

即用synchronized关键字修饰方法。 由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。给一个方法增加synchronized修饰符之后就可以使它成为同步方法,这个方法可以是静态方法和非静态方法,但是不能是抽象类的抽象方法,也不能是接口中的接口方法。

线程在执行同步方法时是具有排它性的。当任意一个线程进入到一个对象的任意一个同步方法时,这个对象的所有同步方法都被锁定了,在此期间,其他任何线程都不能访问这个对象的任意一个同步方法,直到这个线程执行完它所调用的同步方法并从中退出,从而导致它释放了该对象的同步锁之后。

(2)同步代码块

即用synchronized关键字修饰代码块。被该关键字修饰的语句块会自动被加上内置锁,从而实现同步。同步块是通过锁定一个指定的对象,来对同步块中包含的代码进行同步;而同步方法是对这个方法块里的代码进行同步,而这种情况下锁定的对象就是同步方法所属的主体对象自身。如果这个方法是静态同步方法呢?那么线程锁定的就不是这个类的对象了,也不是这个类自身,而是这个类对应的Java.lang.Class类型的对象。同步方法和同步块之间的相互制约只限于同一个对象之间,所以静态同步方法只受它所属类的其它静态同步方法的制约,而跟这个类的实例(对象)没有关系。

如果一个对象既有同步方法,又有同步块,那么当其中任意一个同步方法或者同步块被某个线程执行时,这个对象就被锁定了,其他线程无法在此时访问这个对象的同步方法,也不能执行同步块。

synchronized 关键字用于保护共享数据。请大家注意“共享数据”,你一定要分清哪些数据是共享数据,请看下面的例子:

public class ThreadTest implements Runnable{public synchronized void run(){for(int i=0;i<10;i++) {System.out.print(" " + i);}}public static void main(String[] args) {Runnable r1 = new ThreadTest();Runnable r2 = new ThreadTest();Thread t1 = new Thread(r1);Thread t2 = new Thread(r2);t1.start();t2.start();}}

在这个程序中,run()虽然被加上了synchronized 关键字,但保护的不是共享数据。因为这个程序中的t1,t2 是两个对象(r1,r2)的线程。而不同的对象的数据是不同的,r1,r2 有各自的run()方法,所以输出结果无法预知。

synchronized的目的是使同一个对象的多个线程,在某个时刻只有其中的一个线程可以访问这个对象的synchronized 数据。每个对象都有一个“锁标志”,当这个对象的一个线程访问这个对象的某个synchronized 数据时,这个对象的所有被synchronized 修饰的数据将被上锁(因为“锁标志”被当前线程拿走了),只有当前线程访问完它要访问的synchronized 数据时,当前线程才会释放“锁标志”,这样同一个对象的其它线程才有机会访问synchronized 数据。

public class ThreadTest implements Runnable{public synchronized void run(){for(int i=0;i<10;i++){System.out.print(" " + i);}}public static void main(String[] args){Runnable r = new ThreadTest();Thread t1 = new Thread(r);Thread t2 = new Thread(r);t1.start();t2.start();}}

如果你运行1000 次这个程序,它的输出结果也一定每次都是:0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9。因为这里的synchronized 保护的是共享数据。t1,t2 是同一个对象(r)的两个线程,当其中的一个线程(例如:t1)开始执行run()方法时,由于run()受synchronized保护,所以同一个对象的其他线程(t2)无法访问synchronized 方法(run 方法)。只有当t1执行完后t2 才有机会执行。

示例4:

public class ThreadTest implements Runnable{public void run(){synchronized(this){for(int i=0;i<10;i++){System.out.print(" " + i);}}}public static void main(String[] args){Runnable r = new ThreadTest();Thread t1 = new Thread(r);Thread t2 = new Thread(r);t1.start();t2.start();
}
}

这个程序与示例3 的运行结果一样。在可能的情况下,应该把保护范围缩到最小,可以用示例4 的形式,this 代表“这个对象”。没有必要把整个run()保护起来,run()中的代码只有一个for循环,所以只要保护for 循环就可以了。

示例5:

public class ThreadTest implements Runnable{public void run(){for(int k=0;k<5;k++){System.out.println(Thread.currentThread().getName()+ " : for loop : " + k);}synchronized(this){for(int k=0;k<5;k++) {System.out.println(Thread.currentThread().getName()+ " : synchronized for loop : " + k);}}}public static void main(String[] args){Runnable r = new ThreadTest();Thread t1 = new Thread(r,"t1_name");Thread t2 = new Thread(r,"t2_name");t1.start();t2.start();}
}

运行结果:

t1_name : for loop : 0
t1_name : for loop : 1
t1_name : for loop : 2
t2_name : for loop : 0
t1_name : for loop : 3
t2_name : for loop : 1
t1_name : for loop : 4
t2_name : for loop : 2
t1_name : synchronized for loop : 0
t2_name : for loop : 3
t1_name : synchronized for loop : 1
t2_name : for loop : 4
t1_name : synchronized for loop : 2
t1_name : synchronized for loop : 3
t1_name : synchronized for loop : 4
t2_name : synchronized for loop : 0
t2_name : synchronized for loop : 1
t2_name : synchronized for loop : 2
t2_name : synchronized for loop : 3
t2_name : synchronized for loop : 4

第一个for 循环没有受synchronized 保护。对于第一个for 循环,t1,t2 可以同时访问。运行结果表明t1 执行到了k=2 时,t2 开始执行了。t1 首先执行完了第一个for 循环,此时t2还没有执行完第一个for 循环(t2 刚执行到k=2)。t1 开始执行第二个for 循环,当t1 的第二个for 循环执行到k=1 时,t2 的第一个for 循环执行完了。t2 想开始执行第二个for 循环,但由于t1 首先执行了第二个for 循环,这个对象的锁标志自然在t1 手中(synchronized 方法的执行权也就落到了t1 手中),在t1 没执行完第二个for 循环的时候,它是不会释放锁标志的。所以t2 必须等到t1 执行完第二个for 循环后,它才可以执行第二个for 循环。

使用特殊域变量(Volatile)实现线程同步

volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。如果volatile变量修饰符使用恰当的话,它比synchronized的使用和执行成本更低,因为它不会引起线程上下文的切换和调度。Java内存模型对volatile专门定义了一些特殊的访问规则:

a.volatile关键字为域变量的访问提供了一种免锁机制

b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新

c.因此每次使用该域就要重新计算,而不是使用寄存器中的值

d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量  volatile 修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。 优势:这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。 缘由:Java 语言规范中指出,为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。而 volatile 关键字就是提示 JVM :对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。 使用技巧:在两个或者更多的线程访问的成员变量上使用 volatile 。当要访问的变量已在synchronized 代码块中,或者为常量时,不必使用。 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的 是B。只在某些动作时才进行A和B的同步,因此存在A和B不一致的情况。volatile就是用来避免这种 情况的。 volatile告诉jvm,它所修饰的变量不保留拷贝,直接访问主内存中的(读操作多时使用较好;线程间需要通信,本条做不到) Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。Volatile 变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。 您只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件: 对变量的写操作不依赖于当前值;该变量没有包含在具有其他变量的不变式中。

释放同步监视器的锁定

程序无法显式释放对同步监视器的锁定,线程会在如下几种情况下释放对同步监视器的锁定:

  • 当前线程的同步方法,同步代码块执行结束,当前线程即释放同步监视器;
  • 当线程在同步方法,同步代码块中遇到break、return终止了该代码块、该方法的继续运行,当前线程将会释放同步监视器;
  • 当线程在同步方法,同步代码块中出现了未处理的Error或Exception,导致了该方法、该代码块异常结束时将会释放同步监视器;
  • 当线程在同步方法,同步代码块时,程序执行了同步监视器对象的wait()方法,则当前线程暂停,并释放同步监视器。

在下面情况下,线程不会释放同步监视器:

  • 线程执行同步方法或同步代码块时,程序调用Thread.sleep()、Thread.yield()方法来暂停当前线程的执行,当前线程不会释放同步监视器;
  • 线程执行同步代码块时,其他线程调用了该线程的suspend方法将该线程挂起,该线程不会释放同步监视器。当然,我们应该避免使用suspend和resume方法来控制线程;

总结的有点乱,等有时间再整理下,有不当之处还请指教

参考:

https://www.cnblogs.com/mengyuxin/p/5358364.html

http://blog.csdn.net/ff55c/article/details/6748604

http://developer.51cto.com/art/201509/490965.htm

http://www.itzhai.com/java-based-notebook-thread-synchronization-problem-solving-synchronization-problems-synchronized-block-synchronized-methods.html#read-more

浅谈Java 多线程同步相关推荐

  1. 浅谈Java多线程同步机制之同步块(方法)——synchronized

    在多线程访问的时候,同一时刻只能有一个线程能够用 synchronized 修饰的方法或者代码块,解决了资源共享.下面代码示意三个窗口购5张火车票: 1 package com.jikexueyuan ...

  2. 浅谈Java多线程机制

    浅谈Java多线程机制 (-----文中重点信息将用红色字体凸显-----) 一.话题导入 在开始简述Java多线程机制之前,我不得不吐槽一下我国糟糕的IT界技术分享氛围和不给力的互联网技术解答深度. ...

  3. java 多线程同步_浅谈Java多线程(状态、同步等)

    Java多线程是Java程序员必须掌握的基本的知识点,这块知识点比较复杂,知识点也比较多,今天我们一一来聊下Java多线程,系统的整理下这部分内容. 一.Java中线程创建的三种方式: 1.通过继承T ...

  4. java多线程互斥锁_浅谈Java多线程互斥锁

    为了解决竞争条件带来的问题,我们可以对资源上锁.多个线程共同读写的资源称为共享资源,也叫临界资源.涉及操作临界资源的代码区域称为临界区(Critical Section).同一时刻,只能有一个线程进入 ...

  5. 浅谈Java内存模型、并发、多线程

    浅谈Java内存模型.并发.多线程 Java内存模型(Java Memory Model)是围绕着在并发编程中如何处理原子性,可见性,有序性三个特性而建立的模型. 下面我简单描述一下这三个特性: 原子 ...

  6. java方法区对象类型_浅谈Java内存区域与对象创建过程

    一.java内存区域 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有的区域则 ...

  7. Java可以用到军事方面吗_恭喜遥三运载火箭发射成功, 浅谈 java 在军事方面的运用!...

    恭喜遥三运载火箭发射成功, 浅谈 java 在军事方面的运用! 恭喜长征五号遥三成功发射! 来聊聊军事系统都是用什么语言编写的 长征五号运载火箭, 是中国运载火箭升级换代的重要工程, 作为中国首型大推 ...

  8. 常在池边游,却不曾到池子里一探究竟?浅谈Java线程池

    浅谈Java线程池 线程池,简单来说,就是一个池子嘛,里面养着一群线程,ABCD........,然后你要用的时候,从里面拿一个去用,用完放回去. 一群人要用的时候,每个人都从池子里面拿一个线程,当池 ...

  9. java中单例的应用_浅谈Java中单例模式的几种应用

    目录 浅谈Java中单例模式的几种应用 第一种:懒汉式 第二种:饿汉式 第三种:双重检索式 第四种:注册登记式 第五种:内部类形式 浅谈Java中单例模式的几种应用 日常开发中,为了提高我们系统中对象 ...

最新文章

  1. leetcode算法题--最大平均值和的分组★
  2. tomcat8配置tomcat-users.xml不生效
  3. 在mpvue中引用vant weapp
  4. mybatis教程--实现增删改查的入门教程
  5. [云炬创业基础笔记]第十章企业的利润计划测试6
  6. dmidecode常用的查询
  7. Update From 用法
  8. 1042. 字符统计(20)-PAT乙级真题
  9. php截取多个分割符号_PHP按符号截取字符串的指定部分的实现方法
  10. iOS开发网络篇—发送GET和POST请求(使用NSURLSession)
  11. 电脑端查看CAD图纸也能非常方便
  12. URL和URI的区别与联系
  13. 如何把几张图片合并成一张图片?
  14. Excel函数 - Round函数使用方法
  15. median filter
  16. 《极客与团队》一HRT实战
  17. 汕尾python高手_放飞梦想,不问所得
  18. 哈希表查找 的 平均长度
  19. 浏览器对HTML5中track标签中src属性路径vtt文件错误,Unsafe attempt to load URL vtt
  20. 小议移动订货之南北厂商

热门文章

  1. aspose添加word水印和itextpdf添加pdf水印
  2. 新零售、实体零售都逃不过的刺激战场,靠什么扭转风向?
  3. 全屏网格折叠动画插件 1
  4. Visio对mysql怎么画er图_Microsoft Office Visio如何绘制ER图?Microsoft Office Visio绘制ER图的方法步骤...
  5. 【原创】M3U8文件视频下载工具
  6. [转载]Word中大表格正确跨页断行的设置
  7. [FFmpeg] 水平翻转视频,对称显示播放
  8. Android 自定义View----触摸反馈
  9. 坐标系的种类(机器视觉)
  10. 金融壹账通黄润中:“5G到来将使金融科技取得突破性颠覆”