多线程环境下,必须考虑线程同步的问题,这是因为多个线程同时访问变量或者资源时会有线程争用,比如A线程读取了一个变量,B线程也读取了这个变量,然后他们同时对这个变量做了修改,写回到内存中,由于是同时做修改,就会导致修改的状态不一致.

用一个实际的例子来说明线程同步的必要性:

package cn.outofmemory.locks; public class LockDemo implements Runnable { private int counter = 0; public void run() { int loopTimes = 10000; while (loopTimes > 0) {  counter ++; loopTimes --; } } public static void main(String[] args) throws InterruptedException { LockDemo demo = new LockDemo(); Thread[] threads = new Thread[]{ new Thread(demo), new Thread(demo),new Thread(demo), new Thread(demo),new Thread(demo) }; for (Thread t : threads) { t.start(); } for (Thread t : threads) { t.join(); } System.out.println("demo's counter is " + demo.counter); } }

这段代码中的LockDemo类实现了Runnable接口,在run方法中对其私有变量counter递加了10000次。在main方法中我们首先初始化了一个LockDemo对象,然后初始化了5个线程,这5个线程公用一个LockDemo的实例。
然后我们一次启动这5个线程,然后通过join等待所有线程结束,最后输出demo实例的counter值来。

运行程序,我这儿得到这样一个输出结果:

demo's counter is 44041

本来5个线程每个线程递加10000次,应该得到的结果是50000,而实际的结果是44041.

如果你也运行此程序有可能会得到不一样的结果。这取决于这5个线程造成了多少次的冲突。从我的输出结果看,这段程序的5个线程造成了大约6000次的内存争用冲突。
在实际应用中,这是不可用的。

改进程序,避免冲突

我们可以分析一下,这5个线程的冲突出现在什么地方,他们公用了demo对象,同时对demo对象的成员变量counter做递加,也就是说冲突出现在对counter递加这一步上。
我们在这一步操作上加上synchronzied关键字,让5个线程执行到对counter++这步代码时单独运行,应该就可以解决问题了。

修改后的run方法代码:

    public void run() { int loopTimes = 10000; while (loopTimes > 0) { synchronized (this) { counter ++; } loopTimes --; } }

我们再次运行程序会得到如下确定的输出结果:

demo's counter is 50000

这次得到的结果是符合我们的预期的,我们通过synchronized关键字解决了问题。

synchronized关键字是jvm虚拟机的关键字,在java.util.concurrent.locks命名空间中还有一个Lock接口,和Lock接口的实现类ReentrantLock(可重入锁)。 ReentrantLock可以实现和synchronized关键字相同的功能,而且更为灵活,在极端的情况下性能会更好一些。

我们看下使用可重入锁ReentrantLock解决线程同步的方法:

   private final Lock lock = new ReentrantLock(); public void run() { int loopTimes = 10000; while (loopTimes > 0) { try { lock.lock(); counter ++; } finally { lock.unlock(); } loopTimes --; } }

我们在LockDemo中添加了一个final的成员变量lock,它是一个ReentrantLock的实例。 在run方法中,在counter++这行代码两边加上了try .. finally ..语句,
当线程执行到try块之后,首先通过lock.lock()获得锁,获得锁之后再执行counter++,最后在finally语句块中通过lock的unlock方法释放锁。

我们可以运行修改后的代码,输出如下:

demo's counter is 50000

输出结果符合逻辑预期。

synchronized获得的内部锁存在一定的局限

1. 不能中断一个正在试图获得锁的线程

2. 试图获得锁时不能像trylock那样设定超时时间

3. 每个锁只有单一的条件,不像condition那样可以设置多个

synchronzied关键字和可重入锁ReentrantLock选择的最佳实践:

1. 如果synchronized关键字适合程序,尽量使用它,可以减少代码出错的几率和代码数量
2. 如果特别需要Lock/Condition结构提供的独有特性时,才使用他们
3. 许多情况下可以使用java.util.concurrent包中的一种机制,它会为你处理所有的加锁情况

上一篇:java线程同步:使用Object的wait,notify,notifyAll做线程调度 下一篇:java并发控制:ReentrantLock Condition使用详解
推荐阅读:
  • java并发控制:ReentrantLock Condition使用详解
  • java线程组(ThreadGroup)
  • Java 抽象类
  • [韩顺平]Java从入门到精通第8讲-this.类变量
  • 韩顺平.循序渐进学.java.从入门到精通.第24讲-集合补充.avi
  • 韩顺平.循序渐进学.java.从入门到精通.第40讲-线程.avi
  • java语言从入门到精通——运算符与表达式 07
  • java语言从入门到精通——抽象类和抽象函数 23
  • java语言从入门到精通——Java当中的数组 40
  • JAVA从入门到精通(MLDN) 1.4 安装JAVA开发工具箱——JDK 1.6.0_17多国语
  • 服务幂等以及常用实现方式
支持

2 顶

反对

0 踩

上一篇:java线程同步:使用Object的wait,notify,notifyAll做线程调度 下一篇:java并发控制:ReentrantLock Condition使用详解

转载于:https://www.cnblogs.com/yaowen/p/6133844.html

synchronized关键字,Lock接口以及可重入锁ReentrantLock相关推荐

  1. 并发编程系列之五多线程synchronized是可重复加锁,重入锁

    并发编程系列之五多线程synchronized是可重复加锁,重入锁.对于重入锁的概念就是可以重复的加锁.. 示例1,在同一个类里面进行加锁,不同的方法调用,都一层一层的嵌套进行加锁,示例1演示重入锁的 ...

  2. 并发编程-19AQS同步组件之重入锁ReentrantLock、 读写锁ReentrantReadWriteLock、Condition

    文章目录 J.U.C脑图 ReentrantLock概述 ReentrantLock 常用方法 synchronized 和 ReentrantLock的比较 ReentrantLock示例 读写锁R ...

  3. Java 重入锁 ReentrantLock 原理分析

    1.简介 可重入锁ReentrantLock自 JDK 1.5 被引入,功能上与synchronized关键字类似.所谓的可重入是指,线程可对同一把锁进行重复加锁,而不会被阻塞住,这样可避免死锁的产生 ...

  4. java多线程---重入锁ReentrantLock

    1.定义 重入锁ReentrantLock,支持重入的锁,表示一个线程对资源的重复加锁. 2.底层实现 每个锁关联一个线程持有者和计数器,当计数器为0时表示该锁没有被任何线程持有,那么任何线程都可能获 ...

  5. 重入锁ReentrantLock详解

    重入锁ReentrantLock,顾名思义,就是支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁.除此之外,该锁的还支持获取锁时的公平和非公平性选择. 在AQS实现中,当一个线程调用Mute ...

  6. java中的账户冻结原理_java可重入锁(ReentrantLock)的实现原理

    前言 相信学过java的人都知道 synchronized 这个关键词,也知道它用于控制多线程对并发资源的安全访问,兴许,你还用过Lock相关的功能,但你可能从来没有想过java中的锁底层的机制是怎么 ...

  7. synchronized 与 Reentrant均为可重入锁 区别后者比前者增加了长时等待可中断 设置是否公平锁 绑定多个条件

    ① 两者都是可重入锁 两者都是可重入锁."可重入锁"概念是:自己可以再次获取自己的内部锁.比如一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时 ...

  8. 锁Lock,主要是重入锁和读写锁

    2019独角兽企业重金招聘Python工程师标准>>> 在java多线程中,我们可以使用synchronized关键字来实现线程之间的同步互斥工作.还有一个更优秀的机制去完成这个&q ...

  9. java lock可重入_Java源码解析之可重入锁ReentrantLock

    本文基于jdk1.8进行分析. ReentrantLock是一个可重入锁,在ConcurrentHashMap中使用了ReentrantLock. 首先看一下源码中对ReentrantLock的介绍. ...

  10. 详述重入锁-ReentrantLock

    什么是重入锁? 锁主要用来控制多线程访问的行为,对于同一个线程,如果连续两次对同一把锁进行lock,那么这个线程会被卡死在那里,这样的特性很不好,在实际的开发中,方法之间的调用方式错综复杂,如果不小心 ...

最新文章

  1. Check the value configured in 'zookeeper.znode.parent'
  2. gaokao--选择开发什么项目
  3. 简明python教程电子版-简明Python教程PDF电子书免费下载
  4. 贷款利率最高多少合法?
  5. STARTUPINFO结构
  6. apache mesos_试用Apache Mesos HTTP API获得乐趣和收益
  7. day05-数据类型与操作
  8. nasm实现的用vmware运行自做的linux启动盘的引导代码
  9. Pytorch相关函数详解
  10. php读取Excel xlsx 2007+并导入MySQL
  11. Maven的安装与配置
  12. 上位机和下位机有什么区别和关系?常用上位机软件开发工具介绍
  13. lwj_C#_string类方法 可变字符串
  14. 苹果注册2019款iPhone 预计今秋将发布3款11个版本
  15. php怎么将农历转换成公历,php实现阳历阴历互转的方法
  16. 英特尔的指令集体系结构_Intel MIC初探(一):MIC架构及编程模型概览
  17. STM32F103_study66_The punctual atoms(STM32 Temperature sensor experiment)
  18. 【实验分享】备份IOS文件
  19. 你负责健康快乐长大,我负责学做一个合格的妈妈
  20. SQL中where in的用法

热门文章

  1. c语言转意字符 s,第2章 C语言初探:12、C语言转义字符
  2. python多线程互斥锁_Python中线程互斥锁是什么
  3. C++_运算符重载 再思考
  4. 设计模式---桥接模式(C++实现)
  5. mysql建表时创建索引语句_创建表的时候创建索引
  6. Spring源码之bean的加载(四)获取单例
  7. 【渝粤教育】国家开放大学2018年秋季 8181-22T (1)老年保健按摩 参考试题
  8. 【渝粤教育】电大中专品牌管理与推广 (2)_1作业 题库
  9. Prophet模型预测商品销售量
  10. 【Python实例第29讲】递归的特征排除法