一.synchronized同步方法或者同步块

在了解synchronized关键字的使用方法之前,我们先来看一个概念:互斥锁,顾名思义:能到达到互斥访问目的的锁。

举个简单的例子:如果对临界资源加上互斥锁,当一个线程在访问该临界资源时,其他线程便只能等待。

在Java中,每一个对象都拥有一个锁标记(monitor),也称为监视器,多线程同时访问某个对象时,线程只有获取了该对象的锁才能访问。

在Java中,可以使用synchronized关键字来标记一个方法或者代码块,当某个线程调用该对象的synchronized方法或者访问synchronized代码块时,这个线程便获得了该对象的锁,其他线程暂时无法访问这个方法,只有等待这个方法执行完毕或者代码块执行完毕,这个线程才会释放该对象的锁,其他线程才能执行这个方法或者代码块。

下面通过几个简单的例子来说明synchronized关键字的使用:

1.synchronized方法

下面这段代码中两个线程分别调用insertData对象插入数据:

  1. public class Test {
  2.  
  3. public static void main(String[] args) {
  4. final InsertData insertData = new InsertData();
  5.  
  6. new Thread() {
  7. public void run() {
  8. insertData.insert(Thread.currentThread());
  9. };
  10. }.start();
  11.  
  12. new Thread() {
  13. public void run() {
  14. insertData.insert(Thread.currentThread());
  15. };
  16. }.start();
  17. }
  18. }
  19.  
  20. class InsertData {
  21. private ArrayList<Integer> arrayList = new ArrayList<Integer>();
  22.  
  23. public synchronized void insert(Thread thread){
  24. for(int i=;i<;i++){
  25. System.out.println(thread.getName()+"在插入数据"+i);
  26. arrayList.add(i);
  27. }
  28. }
  29. }

从上输出结果说明,Thread-1插入数据是等Thread-0插入完数据之后才进行的。说明Thread-0和Thread-1是顺序执行insert方法的。

这就是synchronized方法。

不过有几点需要注意:

1)当一个线程正在访问一个对象的synchronized方法,那么其他线程不能访问该对象的其他synchronized方法。这个原因很简单,因为一个对象只有一把锁,当一个线程获取了该对象的锁之后,其他线程无法获取该对象的锁,所以无法访问该对象的其他synchronized方法。

2)当一个线程正在访问一个对象的synchronized方法,那么其他线程能访问该对象的非synchronized方法。这个原因很简单,访问非synchronized方法不需要获得该对象的锁,假如一个方法没用synchronized关键字修饰,说明它不会使用到临界资源,那么其他线程是可以访问这个方法的,

3)如果一个线程A需要访问对象object1的synchronized方法fun1,另外一个线程B需要访问对象object2的synchronized方法fun1,即使object1和object2是同一类型,也不会产生线程安全问题,因为他们访问的是不同的对象,所以不存在互斥问题。

2.synchronized代码块

当在某个线程中执行这段代码块,该线程会获取对象synObject的锁,从而使得其他线程无法同时访问该代码块。

synObject可以是this,代表获取当前对象的锁,也可以是类中的一个属性,代表获取该属性的锁。

  1. 比如上面的insert方法可以改成以下两种形式:
  2.  
  3. class InsertData {
  4. private ArrayList<Integer> arrayList = new ArrayList<Integer>();
  5.  
  6. public void insert(Thread thread){
  7. synchronized (this) {
  8. for(int i=;i<;i++){
  9. System.out.println(thread.getName()+"在插入数据"+i);
  10. arrayList.add(i);
  11. }
  12. }
  13. }
  14. }
  15.  
  16. class InsertData {
  17. private ArrayList<Integer> arrayList = new ArrayList<Integer>();
  18. private Object object = new Object();
  19.  
  20. public void insert(Thread thread){
  21. synchronized (object) {
  22. for(int i=;i<;i++){
  23. System.out.println(thread.getName()+"在插入数据"+i);
  24. arrayList.add(i);
  25. }
  26. }
  27. }
  28. }
  29.  
  30. 从上面可以看出,synchronized代码块使用起来比synchronized方法要灵活得多。因为也许一个方法中只有一部分代码只需要同步,如果此时对整个方法用synchronized进行同步,会影响程序执行效率。而使用synchronized代码块就可以避免这个问题,synchronized代码块可以实现只对需要同步的地方进行同步

另外,每个类也会有一个锁,它可以用来控制对static数据成员的并发访问。

并且如果一个线程执行一个对象的非static synchronized方法,另外一个线程需要执行这个对象所属类的static synchronized方法,此时不会发生互斥现象,因为访问static synchronized方法占用的是类锁,而访问非static synchronized方法占用的是对象锁,所以不存在互斥现象。(对象级别的synchronized,还有类级别的synchronized.锁定的是整 个clz.class)

  1. 看下面这段代码就明白了:
  2.  
  3. public class Test {
  4.  
  5. public static void main(String[] args) {
  6. final InsertData insertData = new InsertData();
  7. new Thread(){
  8. @Override
  9. public void run() {
  10. insertData.insert();
  11. }
  12. }.start();
  13. new Thread(){
  14. @Override
  15. public void run() {
  16. insertData.insert1();
  17. }
  18. }.start();
  19. }
  20. }
  21.  
  22. class InsertData {
  23. public synchronized void insert(){
  24. System.out.println("执行insert");
  25. try {
  26. Thread.sleep();
  27. } catch (InterruptedException e) {
  28. e.printStackTrace();
  29. }
  30. System.out.println("执行insert完毕");
  31. }
  32.  
  33. public synchronized static void insert1() {
  34. System.out.println("执行insert1");
  35. System.out.println("执行insert1完毕");
  36. }
  37. }

执行结果;

第一个线程里面执行的是insert方法,不会导致第二个线程执行insert1方法发生阻塞现象。

下面我们看一下synchronized关键字到底做了什么事情,我们来反编译它的字节码看一下,下面这段代码反编译后的字节码为:

  1. public class InsertData {
  2. private Object object = new Object();
  3.  
  4. public void insert(Thread thread){
  5. synchronized (object) {
  6.  
  7. }
  8. }
  9.  
  10. public synchronized void insert1(Thread thread){
  11.  
  12. }
  13.  
  14. public void insert2(Thread thread){
  15.  
  16. }
  17. }

  从反编译获得的字节码可以看出,synchronized代码块实际上多了monitorenter和monitorexit两条指令。monitorenter指令执行时会让对象的锁计数加1,而monitorexit指令执行时会让对象的锁计数减1,其实这个与操作系统里面的PV操作很像,操作系统里面的PV操作就是用来控制多个线程对临界资源的访问。对于synchronized方法,执行中的线程识别该方法的 method_info 结构是否有 ACC_SYNCHRONIZED 标记设置,然后它自动获取对象的锁,调用方法,最后释放锁。如果有异常发生,线程自动释放锁。

  

  有一点要注意:对于synchronized方法或者synchronized代码块,当出现异常时,JVM会自动释放当前线程占用的锁,因此不会由于异常导致出现死锁现象。

二.java单例模式(双重检查加锁)的原因

双重检查加锁,只有在第一次实例化时,才启用同步机制,提高了性能。

  1. public class Singleton{
  2. private static Singleton instance = null;//是否是final的不重要,因为最多只可能实例化一次。
  3. private Singleton(){}
  4. public static Singleton getInstance(){
  5. if(instance == null){
  6. //双重检查加锁,只有在第一次实例化时,才启用同步机制,提高了性能。
  7. synchronized(Singleton.Class){
  8. if(instance == null){
  9. instance = new Singleton();
  10. }
  11. }
  12. }
  13. return instance;
  14. }

Java并发编程(四)synchronized的更多相关文章

  1. Java并发编程:Synchronized及其实现原理

    Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...

  2. Java并发编程:Synchronized底层优化(偏向锁、轻量级锁)

    Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...

  3. Java并发编程:synchronized

    Java并发编程:synchronized 虽然多线程编程极大地提高了效率,但是也会带来一定的隐患.比如说两个线程同时往一个数据库表中插入不重复的数据,就可能会导致数据库中插入了相同的数据.今天我们就 ...

  4. 【转】Java并发编程:synchronized

    一.什么时候会出现线程安全问题? 在单线程中不会出现线程安全问题,而在多线程编程中,有可能会出现同时访问同一个资源的情况,这种资源可以是各种类型的资源:一个变量.一个对象.一个文件.一个数据库表等,而 ...

  5. 4、Java并发编程:synchronized

    Java并发编程:synchronized 虽然多线程编程极大地提高了效率,但是也会带来一定的隐患.比如说两个线程同时往一个数据库表中插入不重复的数据,就可能会导致数据库中插入了相同的数据.今天我们就 ...

  6. 【转】Java并发编程:Synchronized及其实现原理

    一.Synchronized的基本使用 Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法.Synchronized的作用主要有三个:(1)确保线程互斥的访问同步 ...

  7. Java 并发编程(四):如何保证对象的线程安全性

    01.前言 先让我吐一句肺腑之言吧,不说出来会憋出内伤的.<Java 并发编程实战>这本书太特么枯燥了,尽管它被奉为并发编程当中的经典之作,但我还是忍不住.因为第四章"对象的组合 ...

  8. 【Java并发编程】synchronized相关面试题总结

    目录 说说自己对于synchronized关键字的了解 synchronized关键字的三种使用 synchronized关键字的底层原理 JDK1.6之后对synchronized关键字进行的优化 ...

  9. 【Java并发编程四】关卡

    一.什么是关卡? 关卡类似于闭锁,它们都能阻塞一组线程,直到某些事件发生. 关卡和闭锁关键的不同在于,所有线程必须同时到达关卡点,才能继续处理.闭锁等待的是事件,关卡等待的是其他线程. 二.Cycli ...

  10. Java并发编程实例(synchronized)

    此处用一个小程序来说明一下,逻辑是一个计数器(int i):主要的逻辑功能是,如果同步监视了资源i,则不输出i的值,但如果没有添加关键字synchronized,因为是两个线程并发执行,所以会输出i的 ...

随机推荐

  1. requsets模块的学习

    requests模块的学习 使用之前 pip install requests 发起get,post,请求获取响应 response = requests.get(url,headers) # 发起g ...

  2. 从github clone文件: Failed to receive SOCKS4 connect request ack.

    安装了代理,能上网,也能从github上下载文件,就是无法从github上clone文件, 查了很久资料后,终于发现使用sudo可以解决问题.不过,不知道原因是什么? 比如:git clone htt ...

  3. 外媒:比特币大陆将于9月IPO 规模或高达180亿美元

    看看你们坚持买的比特币是否值得? 北京时间8月13日上午消息,据CoinDesk获得的文件,比特币大陆将于今年9月申请首次公开募股(IPO),其规模可能高达180亿美元,市值预计在400亿美元到500 ...

  4. 【LeetCode】7. 整数反转python3

    题目 给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转. 示例 1:输入: 123 输出: 321  示例 2:输入: -123 输出: -321 示例 3:输入: 120 输出 ...

  5. .net core内部分享ppt

    Microsoft .NET 自 2002 年发行 v1.0 以来,已经过了近 14 个年头,在这 14 年里面,.NET 日渐成熟并成为 Microsoft 的重要开发平台之一,只要是在 Windo ...

  6. TCP/IP 笔记 - 域名解析和域名系统

    由于IP地址的烦琐导致的记忆和使用困难,互联网支持使用主机名称来识别包括客户机和服务器在内的主机.同时为了使用一系列协议,主机名称通过称为"名称解析"的过程转换成对应IP地址. 互 ...

  7. PostgreSQL 数据类型

    数值类型 数值类型由两个字节,4字节和8字节的整数,4字节和8字节的浮点数和可选精度的小数.下表列出了可用的类型. www.yiibai.com Name Storage Size Descripti ...

  8. JS nodeList转数组,兼容IE低版本

    一.前言 nodeList转数组貌似很少会这样去操作,但我在做图片懒加载时,我获取了所有需要做懒加载的img元素,也就是一个NodeList对象,打个比方: 对这些元素进行src修改后,我想将此项从N ...

  9. git第七节---git merge和git rebase

    # git merge和git rebase 都可以进行分支合并 #git merge 合并后保留记录两个分支的记录 #git rebase合并后会展示成一个分支的记录,另一个分支的提交实际生成了一个 ...

  10. 史上最全的 Python 3 类型转换指南

    int 支持转换为 int 类型的,仅有 float.str.bytes,其他类型均不支持. float -> int 会去掉小数点及后面的数值,仅保留整数部分. int(-12.94) # - ...