JAVA并发与锁相关的使用
来源:互联网 发布:安卓纪元 矩阵潜袭 编辑:程序博客网 时间:2024/06/06 17:39
JAVA比C++优雅的地方就在于对于并发拥有非常完美的生态(虽然boost库也提供了很多成熟的功能,但是仍然无法和java相比),而且还在不断进化。当C++程序员还在研究互斥被虐过100遍的时候,JAVA不需要任何知识便可愉快的写出强壮的并发代码。
这段在我的系统上运行消耗了55%的CPU,并消耗了约30秒的时间 (此处数据仅用作与其他方法对比,不做数据报告使用)。但是,由于而同样的单线程计算1000000000次加法操作,仅仅消耗了20%的CPU与约19秒的时间。而中间相差的资源则浪费在锁的竞争与线程的挂起状态切换上。所以, 假如我们的操作对锁的持有非常短,则看起来,这种方法是非常不划算的。为了避免这种情况,可以使用自旋锁来避免频繁挂起线程。
换成:
但是,偶然的一次我在使用lock的时候出现了一个很大的问题:当我对锁持有的时间很短,但是却非常频繁的访问的时候,性能却消耗在了锁的竞争与线程的挂起,眼看这CPU跑满但效率却不忍直视。这个时候我才意识到并发是个非常复杂的概念。我赶快去查找相关资料,做了一些整理。
同步
首先 同步关键字仍然是首选,编译器会自动优化达到性能最佳化(尤其是1.7+版本中)。尽可能的减少同步块内的逻辑复杂度,同时也要尽量避免在同步块内调用其他含有同步语句的方法,规避死锁风险。
LOCK
JAVA的锁可以说是重量级的锁,在锁的竞争和线程的挂起中会消耗一些CPU资源,如果我们在加锁后的操作非常复杂,无可厚非,这种消耗在整体的占比中并不高。但是如果我们加锁的业务非常简单,那么浪费的CPU会非常可观。做一个测试,让CPU不停的竞争锁:
private static int i = 0; private static ReentrantLock lock = new ReentrantLock(); private static CountDownLatch latch = new CountDownLatch(8); public static class TESTLOCK implements Runnable{public void run() {while(true){try{lock.lock();if(i > 1000000000){break;}i += 1;}finally{lock.unlock();}}latch.countDown();} } public static void main(String[] parms) throws UnsupportedEncodingException{ long t = System.currentTimeMillis(); for(int i = 0 ; i < 8 ; i++){ new Thread(new TESTLOCK()).start(); } try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("USE:" + (System.currentTimeMillis() - t)); }
这段在我的系统上运行消耗了55%的CPU,并消耗了约30秒的时间 (此处数据仅用作与其他方法对比,不做数据报告使用)。但是,由于而同样的单线程计算1000000000次加法操作,仅仅消耗了20%的CPU与约19秒的时间。而中间相差的资源则浪费在锁的竞争与线程的挂起状态切换上。所以, 假如我们的操作对锁的持有非常短,则看起来,这种方法是非常不划算的。为了避免这种情况,可以使用自旋锁来避免频繁挂起线程。
将上文中代码
lock.lock();
换成:
while(!lock.tryLock()){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }}这次,我的CPU消耗下降到了20%,消耗时间20秒。可以说已经接近了无锁的水平,但是这里会有一个很大的缺陷:由于SLEEP后,其他线程会持续的操作对象,从而使SLEEP的线程变相的失去了优先级,所以对一些场景非常不利。比如说整个业务时间非常短,则竞争一次CPU后往往可以执行很多次业务才会再次让其他线程参与竞争。所以这种实现其实只是大幅度的降低了锁竞争的次数,在一些场景中,线程往往白白放弃了CPU,不能发挥出全部性能。
将上文中代码
lock.lock();换成
while(!lock.tryLock()){ Thread.yield();}由于yield只是让出了CPU使用权,重新进入CPU请求队列,所以对优先级影响并不大,但是有一个很大的缺陷:如果操作对锁的持有非常频繁,而且加锁的逻辑在整个业务流程中占据了较大一个比重(如上文中几乎是100%的比重),则CPU可能在连续的空跑中,反而会浪费更多的CPU资源,如上文代码在此时消耗了75%的CPU与约24秒的时间。但是如果在另外一种场景下,加锁的业务逻辑占比非越小,性能越好。
LockSupport
同步原语,特点是直接对线程本身操作,不会产生死锁,而且unpark可以在park之前,非常灵活,但是很多场景并不适用。
CountDownLatch
wait/notify
。。。未完,整理中。。。
0 0
- JAVA并发与锁相关的使用
- 并发相关的Java library
- Java并发与线程相关资源汇总
- 并发编程学习总结(八) :java中synchronized关键字使用详解 对象锁的相关条件的使用(2)
- 【java并发】线程锁技术的使用
- 关于JAVA多线程并发synchronized的测试与合理使用
- 关于JAVA多线程并发synchronized的测试与合理使用
- 关于JAVA多线程并发synchronized的测试与合理使用
- Java并发编程核心方法与框架-Semaphore的使用
- Java并发编程核心方法与框架-exchanger的使用
- Java并发编程核心方法与框架-CountDownLatch的使用
- Java并发编程核心方法与框架-CyclicBarrier的使用
- Java并发编程核心方法与框架-phaser的使用
- Java并发编程核心方法与框架-Executors的使用
- Java并发编程核心方法与框架-TheadPoolExecutor的使用
- Java并发编程核心方法与框架-CompletionService的使用
- Java并发编程核心方法与框架-ExecutorService的使用
- Java并发编程核心方法与框架-ScheduledExecutorService的使用
- 转发代理服务器与反向代理服务器
- Android插件化学习之路(三)之调用外部.dex文件中的代码
- 作用域及auto、static、const、extern的用法和区别
- win7、win8、win10下利用计算机命令对电脑进行定时关机
- 哈希表总结
- JAVA并发与锁相关的使用
- MyEclipse 快捷键方法
- java 正则表达式(Pattern ,Matcher)的使用
- 11月18日 & 11月19日课堂笔记
- php源码之路第六章第五节 ( 写时复制(Copy On Write))
- 谁是凶手?
- 让windows python支持ORACLE之cx_Oracle-手记
- Linux Icon Customization
- 课堂笔记八