关于JAVA多线程并发synchronized的测试与合理使用
在项目开发中, 或许会碰到JAVA的多线程处理, 为保证业务数据的正常, 必须加上锁机制, 常用的处理方法一般是加上synchronized关键字, 目前JDK版本对synchronized已经做了很好的优化, 我们不用再考虑其性能, 但在实际使用中, 往往由于处理不当, 导致系统性能的严重下降, 那么该如何合理的使用synchronized, 必须对其使用方式有个全面了解, 在网上搜寻的资料, 给出的是四种使用方式, 其实可总结为两种, 一个是同步代码块, 一个是同步方法体, 那么该如何使用, 请看下面的测试:
准备两个方法, 对同一个变量做加法, 再对每个方法, 分别开十个线程执行:
[java]
public class ThreadUnit
{
private int i = 0;
private Object obj1 = new Object();
private Object obj2 = new Object();
public synchronized Integer doAdd1(Long start) throws Exception
{
Thread.sleep(100);
++i;
Thread.sleep(100);
return i;
}
public Integer doAdd2(Long start) throws Exception
{
Thread.sleep(100);
++i;
Thread.sleep(100);
return i;
}
}
相关代码:
[java]
// 十个线程同时执行方法2
for (int i = 0; i < 10; i++)
{
new Thread(new MessageThread(1, threadUnit))。start();
}
// 十个线程同时执行方法2
for (int j = 0; j < 10; j++)
{
new Thread(new MessageThread(2, threadUnit))。start();
}
线程处理:
[java]
public void run()
{
try
{
if (operate == 2)
{
long start = System.currentTimeMillis();
int i = threadUnit.doAdd2(start);
long takeTime = System.currentTimeMillis() - start;
System.out.println("doAdd2() => i=" + i + ", spendTime=" + takeTime + "ms");
spendTime += takeTime;
}
else
{
long start = System.currentTimeMillis();
int i = threadUnit.doAdd1(start);
long takeTime = System.currentTimeMillis() - start;
System.out.println("doAdd1() => i=" + i + ", spendTime=" + takeTime + "ms");
spendTime += takeTime;
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
运行结果:
1. 在两个方法体上都加上synchronized
[java]
public synchronized Integer doAdd1(Long start) throws Exception
[java]
public synchronized Integer doAdd2(Long start) throws Exception
执行结果:
[html]
doAdd1() => i=1, spendTime=203ms
doAdd2() => i=2, spendTime=406ms
doAdd2() => i=3, spendTime=609ms
doAdd2() => i=4, spendTime=796ms
doAdd2() => i=5, spendTime=1000ms
doAdd2() => i=6, spendTime=1203ms
doAdd2() => i=7, spendTime=1406ms
doAdd2() => i=8, spendTime=1609ms
doAdd2() => i=9, spendTime=1812ms
doAdd2() => i=10, spendTime=2015ms
doAdd2() => i=11, spendTime=2218ms
doAdd1() => i=12, spendTime=2406ms
doAdd1() => i=13, spendTime=2609ms
doAdd1() => i=14, spendTime=2812ms
doAdd1() => i=15, spendTime=3015ms
doAdd1() => i=16, spendTime=3218ms
doAdd1() => i=17, spendTime=3421ms
doAdd1() => i=18, spendTime=3625ms
doAdd1() => i=19, spendTime=3828ms
doAdd1() => i=20, spendTime=4015ms
花费时间:42226ms
都是有序执行, 变量值没有产生错乱, 但花费时间42226ms
2.在doAdd1方法上加上synchronized, doAdd2不加。
[java]
public synchronized Integer doAdd1(Long start) throws Exception
执行结果:
[java]
doAdd1方法加上synchronized:
doAdd1() => i=9, spendTime=204ms
doAdd2() => i=9, spendTime=188ms
doAdd2() => i=9, spendTime=188ms
doAdd2() => i=9, spendTime=188ms
doAdd2() => i=9, spendTime=188ms
doAdd2() => i=9, spendTime=188ms
doAdd2() => i=9, spendTime=188ms
doAdd2() => i=9, spendTime=188ms
doAdd2() => i=9, spendTime=188ms
doAdd2() => i=9, spendTime=188ms
doAdd2() => i=9, spendTime=188ms
doAdd1() => i=10, spendTime=391ms
doAdd1() => i=11, spendTime=610ms
doAdd1() => i=12, spendTime=813ms
doAdd1() => i=13, spendTime=1016ms
doAdd1() => i=14, spendTime=1219ms
doAdd1() => i=15, spendTime=1422ms
doAdd1() => i=16, spendTime=1610ms
doAdd1() => i=17, spendTime=1813ms
doAdd1() => i=18, spendTime=2016ms
花费时间:12994ms
doAdd2方法瞬间执行完成, 之后doAdd1方法则是串行有序执行。 这时doAdd2方法获取的变量值已经错乱, doAdd1获取的正常。 花费时间:12994ms
3. 两个方法在都没使用synchronized前的情况:
执行结果:
[java]
doAdd1() => i=16, spendTime=203ms
doAdd1() => i=16, spendTime=203ms
doAdd1() => i=16, spendTime=203ms
doAdd1() => i=16, spendTime=203ms
doAdd1() => i=16, spendTime=203ms
doAdd1() => i=16, spendTime=203ms
doAdd1() => i=16, spendTime=203ms
doAdd1() => i=16, spendTime=203ms
doAdd1() => i=16, spendTime=203ms
doAdd2() => i=16, spendTime=203ms
doAdd2() => i=16, spendTime=203ms
doAdd2() => i=16, spendTime=203ms
doAdd1() => i=16, spendTime=203ms
doAdd2() => i=16, spendTime=203ms
doAdd2() => i=16, spendTime=203ms
doAdd2() => i=16, spendTime=203ms
doAdd2() => i=16, spendTime=203ms
doAdd2() => i=16, spendTime=203ms
doAdd2() => i=16, spendTime=203ms
doAdd2() => i=16, spendTime=203ms
花费时间:4060ms
可以看到, 两个方法的变量值获取已经错乱, 但花费时间最少4060ms
4. 使用同步块, 在两个方法内采用不同的对象锁:
doAdd1:
[java]
synchronized (obj1)
{
Thread.sleep(100);
++i;
Thread.sleep(100);
return i;
}
doAdd2:
[java]
synchronized (obj2)
{
Thread.sleep(100);
++i;
Thread.sleep(100);
return i;
}
执行结果:
[html]
doAdd1() => i=2, spendTime=203ms
doAdd2() => i=2, spendTime=203ms
doAdd1() => i=4, spendTime=406ms
doAdd2() => i=4, spendTime=406ms
doAdd1() => i=6, spendTime=609ms
doAdd2() => i=6, spendTime=609ms
doAdd1() => i=8, spendTime=812ms
doAdd2() => i=8, spendTime=812ms
doAdd1() => i=10, spendTime=1000ms
doAdd2() => i=10, spendTime=1015ms
doAdd1() => i=12, spendTime=1203ms
doAdd2() => i=12, spendTime=1203ms
doAdd1() => i=14, spendTime=1406ms
doAdd2() => i=14, spendTime=1406ms
doAdd1() => i=16, spendTime=1609ms
doAdd2() => i=16, spendTime=1609ms
doAdd1() => i=18, spendTime=1812ms
doAdd2() => i=18, spendTime=1812ms
doAdd1() => i=20, spendTime=2015ms
doAdd2() => i=20, spendTime=2015ms
花费时间:22165ms
两个方法有序交替执行, 互不影响, 花费时间:22165ms, 相对加锁同一对象执行的时间缩短。
5. 使用同步块, 使用方法参数作为对象锁:
[java]
public Integer doAdd1(Long start) throws Exception
{
synchronized (start)
{
Thread.sleep(100);
++i;
Thread.sleep(100);
return i;
}
}
执行结果:
[java]
doAdd1() => i=15, spendTime=203ms
doAdd1() => i=15, spendTime=203ms
doAdd1() => i=15, spendTime=203ms
doAdd1() => i=15, spendTime=203ms
doAdd1() => i=15, spendTime=203ms
doAdd1() => i=15, spendTime=203ms
doAdd1() => i=15, spendTime=203ms
doAdd2() => i=15, spendTime=203ms
doAdd2() => i=15, spendTime=203ms
doAdd1() => i=15, spendTime=203ms
doAdd1() => i=15, spendTime=203ms
doAdd2() => i=15, spendTime=203ms
doAdd2() => i=15, spendTime=203ms
doAdd2() => i=15, spendTime=203ms
doAdd2() => i=15, spendTime=203ms
doAdd2() => i=15, spendTime=203ms
doAdd2() => i=15, spendTime=203ms
doAdd2() => i=15, spendTime=203ms
doAdd1() => i=15, spendTime=203ms
doAdd2() => i=15, spendTime=203ms
花费时间:4060ms
执行效果和第三种情况相同, 每个参数作为不同的对象, 即便加上synchronized也不能起到锁的效果。
6. 把调用的类改为静态类, 只在一个方法上加锁:
加锁doAdd1方法:
[java]
public static synchronized Integer doAdd1(Long start) throws Exception
执行结果:
[html]
doAdd1() => i=9, spendTime=203ms
doAdd2() => i=9, spendTime=203ms
doAdd2() => i=9, spendTime=203ms
doAdd2() => i=9, spendTime=203ms
doAdd2() => i=9, spendTime=203ms
doAdd2() => i=9, spendTime=203ms
doAdd2() => i=9, spendTime=203ms
doAdd2() => i=9, spendTime=203ms
doAdd2() => i=9, spendTime=203ms
doAdd2() => i=9, spendTime=203ms
doAdd2() => i=9, spendTime=203ms
doAdd1() => i=10, spendTime=406ms
doAdd1() => i=11, spendTime=609ms
doAdd1() => i=12, spendTime=812ms
doAdd1() => i=13, spendTime=1015ms
doAdd1() => i=14, spendTime=1218ms
doAdd1() => i=15, spendTime=1406ms
doAdd1() => i=16, spendTime=1609ms
doAdd1() => i=17, spendTime=1812ms
doAdd1() => i=18, spendTime=2015ms
花费时间:13135ms
和第二种情形类似, 没有加锁的doAdd2方法瞬间执行完成, doAdd1方法则是串行有序执行。
总结:
1. synchronized关键在于锁定的对象, 如果是同一对象, 那么所有执行线程, 必须等待对象锁释放后才能执行, 如果是不同对象, 那么只对各对象所关联的线程生效。
2. synchronized若加在方法体上, 默认锁定的是对象本身。 对于所有加锁的方法, 都是按照串行规则有序执行, 对于没有加锁的方法, 不受任何影响, 静态类同理。
3. 合理使用synchronized, 既要保证稳定性, 又要保证性能, 需要在两者间作出权衡, 尽量把synchronized范围细度化, 合理控制业务处理流程; 对象操作原子化, 减少锁的使用;
不要盲目在方法体上加synchronized关键字, 如果每个方法负责处理的是不同业务, 那么尽量采用第四种情形, 使用不同的对象锁处理, 而不是锁定整个对象。
关于JAVA多线程并发synchronized的测试与合理使用的更多相关文章
- java 多线程并发 synchronized 同步机制及方式
2. 锁机制 3. 并发 Excutor框架 4. 并发性与多线程介绍 1. synchronized 参考1. synchronized 分两种方式进行线程的同步:同步块.同步方法 1. 方法同步 ...
- Java 多线程并发编程一览笔录
Java 多线程并发编程一览笔录 知识体系图: 1.线程是什么? 线程是进程中独立运行的子任务. 2.创建线程的方式 方式一:将类声明为 Thread 的子类.该子类应重写 Thread 类的 run ...
- Java多线程并发技术
Java多线程并发技术 参考文献: http://blog.csdn.net/aboy123/article/details/38307539 http://blog.csdn.net/ghsau/a ...
- java 多线程并发问题总结
java 多线程并发主要通过关键字synchronized实现 Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并发线程访问 ...
- Java多线程并发05——那么多的锁你都了解了吗
在多线程或高并发情境中,经常会为了保证数据一致性,而引入锁机制,本文将为各位带来有关锁的基本概念讲解.关注我的公众号「Java面典」了解更多 Java 相关知识点. 根据锁的各种特性,可将锁分为以下几 ...
- Java多线程并发07——锁在Java中的实现
上一篇文章中,我们已经介绍过了各种锁,让各位对锁有了一定的了解.接下来将为各位介绍锁在Java中的实现.关注我的公众号「Java面典」了解更多 Java 相关知识点. 在 Java 中主要通过使用sy ...
- Java多线程-并发容器
Java多线程-并发容器 在Java1.5之后,通过几个并发容器类来改进同步容器类,同步容器类是通过将容器的状态串行访问,从而实现它们的线程安全的,这样做会消弱了并发性,当多个线程并发的竞争容器锁的时 ...
- Java多线程-同步:synchronized 和线程通信:生产者消费者模式
大家伙周末愉快,小乐又来给大家献上技术大餐.上次是说到了Java多线程的创建和状态|乐字节,接下来,我们再来接着说Java多线程-同步:synchronized 和线程通信:生产者消费者模式. 一.同 ...
- 从火箭发场景来学习Java多线程并发闭锁对象
从火箭发场景来学习Java多线程并发闭锁对象 倒计时器场景 在我们开发过程中,有时候会使用到倒计时计数器.最简单的是:int size = 5; 执行后,size—这种方式来实现.但是在多线程并发的情 ...
随机推荐
- 1391: [Ceoi2008]order
有N个工作,M种机器,每种机器你可以租或者买过来. 每个工作包括若干道工序,每道工序需要某种机器来完成,你可以通过购买或租用机器来完成. 现在给出这些参数,求最大利润 Input 第一行给出 N,M( ...
- Opencv算法运行时间
使用getTickCount() 需要导入命名空间cv,using namespace cv; double t = getTickCount(); funciont(); double tm = ( ...
- python IDE之sublime真是个好东东
简捷轻爽,代码自动补齐.很好用. python3 { "shell_cmd": "python3 -u \"$file\"", " ...
- 基于 SSH 框架的 Criteria 和 DetachedCriteria 多条件查询
Hibernate 定义了 CriteriaSpecification 接口规范用来完成面向对象的条件查询,Criteria 和 DetachedCriteria 就是 CriteriaSpecifi ...
- CodeForces - 600C Make Palindrome 贪心
A string is called palindrome if it reads the same from left to right and from right to left. For ex ...
- Ajax与传统Web开发的区别
基本概念 1.1,Ajax AJAX:即“Asynchronous Javascript And XML”(异步的JavaScript和XML),是指一种创建交互式网页应用的网页开发技术,尤其是在一种 ...
- 第一个ASP.NET MVC应用程序
首先打开vs2015 文件->新建->项目 选择模版选empty,下面[为下项添加文件夹和核心引用]勾选mvc 点击确定就好
- luoguP4491 [HAOI2018]染色 广义容斥原理 + FFT
非常明显的摆了一个NTT模数.... 题目中求恰好\(k\),那么考虑求至少\(k\) 记\(g(k)\)表示至少\(k\)中颜色出现了恰好\(S\)次 那么,\[g(k) = \binom{M}{k ...
- 破解神器Hashcat使用简介
0x00 背景 目前GPU的速度越来越快,使用GPU超强的运算速度进行暴力密码破解也大大提高了成功率,曾经看到老外用26块显卡组成的分布式破解神器让我羡慕不已.要说目前最好的GPU破解HASH的软件, ...
- BZOJ 2743: [HEOI2012]采花 离线树状数组
2743: [HEOI2012]采花 题目连接: http://www.lydsy.com/JudgeOnline/problem.php?id=2743 Description 萧芸斓是Z国的公主, ...