Java高并发之从零到放弃
前言
本篇主要讲解如何去优化锁机制
或者克服多线程因为锁可导致性能下降的问题
ThreadLocal线程变量
有这样一个场景,前面是一大桶水,10个人去喝水,为了保证线程安全,我们要在杯子上加锁
导致大家轮着排队喝水,因为加了锁的杯子是同步的,只能有一个人拿着这个唯一的杯子喝水
这样子大家都喝完一杯水需要很长的时间
如果我们给每个人分发一个杯子呢?是不是每人喝到水的时间缩小到了十分之一
多线程并发也是一个道理
在每个Thread中都有自己的数据存放空间(ThreadLocalMap)
而ThreadLocal就是在当前线程的存放空间中存放数据
下面这个例子,在每个线程中存放一个arraylist,而不是大家去公用一个arraylist
public class ThreadLocalTest {
public static ThreadLocal threadLocal = new ThreadLocal();
public static ArrayList list = new ArrayList();
public static class Demo implements Runnable {
private int i;
public Demo(int i) {
this.i = i;
}
@Override
public void run() {
list.add(i);
threadLocal.set(list);
System.out.println(threadLocal.get());
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService es = Executors.newFixedThreadPool(5);
for (int j = 0; j < 200; j++) {
es.execute(new Demo(j));
}
Thread.sleep(3000);
System.out.println(list.size());
es.shutdown();
}
}
在每个线程内部有一块存储区域叫做ThreadLocalMap
可以看到,ThreadLocal采用set,get存取值方式
只有线程完全关闭时,在ThreadLocalMap中的数据才会被GC回收
这时有一个值得考虑的问题
我们使用线程池来开发的时候,线程池中的线程并不会关闭,它只是处于空闲状态
也就是说,我们如果把过大的数据存储在当前线程的ThreadLocalMap中,线程不断的调用,被空闲...
最后会导致内存溢出
解决方法是当不需要这些数据时
使用ThreadLocal.remove()方法将变量给移除
CAS操作
还有一种脱离锁的机制,那就是CAS
CAS带着三个变量,分别是:
V更新变量:需要返回的变量
E预期值:原来的值
N新值,传进来的新变量
只有当预期值和新值相等时,才会把V=N,如果不相等,说明该操作会让数据无法同步
根据上面的解释,大概就能知道CAS其实也是在保护数据的同步性
当多个线程进行CAS操作时,可想只有一个线程能成功更新,之后其它线程的E和V会不地进行断比较
所以CAS的同步锁的实现是一样的
CAS操作的并发包在Atomic包中,atomic实现了很多类型
不管是AtomicInteger还是AtomicReference,都有相同点,请观察它们的源码:
private volatile V value; private static final long valueOffset;
以上是AtomicReferenc
private volatile int value; private static final long valueOffset;
以上是AtomicIntege
都有value,这是它们的当前实际值
valueOffset保存的是value的偏移量
下面给出一个简单的AtomicIntege例子:
public class AtomicTest {
public static AtomicInteger atomicInteger = new AtomicInteger();
//public static AtomicReference atomicReference = new AtomicReference();
public static class Demo implements Runnable{
@Override
public void run() {
for (int j=0;j<1000;j++){
atomicInteger.incrementAndGet(); //当前值加1并且返回当前值
}
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService es = Executors.newFixedThreadPool(10);
for (int i =0;i<10;i++){
es.submit(new Demo());
}
Thread.sleep(5000);
System.out.println(atomicInteger);
}
}
你试着执行一下,如果打印出10000说明线程安全
使用CAS操作比同步锁拥有更好的性能
我们来看下incrementAndGet()的源码:
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
来看下getAndAddInt()源码:
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
这里有一个循环,再细看源码发现是native的,虽然看不到原生代码,但是可以看出它这里做了一个CAS操作,不断地进行多个变量的比较,只有预设值和新值相等时,才跳出循环
var5就是需要更新的变量,var1和var2是预设值和新值
死锁
讲了那么多无锁的操作,我们来看一下一个死锁的现象
两个线程互相占着对方想得到的锁,就会出现死锁状况
public class DeadLock extends Thread{
protected String suo;
public static String zuo = new String();
public static String you = new String();
public DeadLock(String suo){
this.suo=suo;
}
@Override
public void run(){
if (suo==zuo){
synchronized (zuo){
System.out.println("拿到了左,正在拿右......");
synchronized (you){
System.out.println("拿到了右,成功了");
}
}
}
if (suo==you){
synchronized (you){
System.out.println("拿到了右,正在拿左......");
synchronized (zuo){
System.out.println("拿到了zuo,成功了");
}
}
}
}
public static void main(String[] args) throws InterruptedException {
for (int i=0;i<10000;i++){
DeadLock t1 = new DeadLock(zuo);
DeadLock t2 = new DeadLock(you);
t1.start();t2.start();
}
Thread.sleep(50000);
}
}
如图:

出现了两个线程的死锁现象,所以说去锁不仅能提升性能,也能防止死锁的产生。
本文地址https://segmentfault.com/a/1190000012218687
更多参考内容:http://www.roncoo.com/article/index
Java高并发之从零到放弃的更多相关文章
- Java高并发之锁优化
本文主要讲并行优化的几种方式, 其结构如下: 锁优化 减少锁的持有时间 例如避免给整个方法加锁 public synchronized void syncMethod(){ othercode1(); ...
- java高并发之线程池
Java高并发之线程池详解 线程池优势 在业务场景中, 如果一个对象创建销毁开销比较大, 那么此时建议池化对象进行管理. 例如线程, jdbc连接等等, 在高并发场景中, 如果可以复用之前销毁的对 ...
- java高并发之锁的使用以及原理浅析
锁像synchronized同步块一样,是一种线程同步机制.让自Java 5开始,java.util.concurrent.locks包提供了另一种方式实现线程同步机制——Lock.那么问题来了既然都 ...
- Java高并发之无锁与Atomic源码分析
目录 CAS原理 AtomicInteger Unsafe AtomicReference AtomicStampedReference AtomicIntegerArray AtomicIntege ...
- 1.6 JAVA高并发之线程池
一.JAVA高级并发 1.5JDK之后引入高级并发特性,大多数的特性在java.util.concurrent 包中,是专门用于多线程发编程的,充分利用了现代多处理器和多核心系统的功能以编写大规模并发 ...
- Java高并发之线程池详解
线程池优势 在业务场景中, 如果一个对象创建销毁开销比较大, 那么此时建议池化对象进行管理. 例如线程, jdbc连接等等, 在高并发场景中, 如果可以复用之前销毁的对象, 那么系统效率将大大提升. ...
- Java高并发之设计模式
本文主要讲解几种常见并行模式, 具体目录结构如下图. 单例 单例是最常见的一种设计模式, 一般用于全局对象管理, 比如xml配置读写之类的. 一般分为懒汉式, 饿汉式. 懒汉式: 方法上加synchr ...
- Java高并发之线程基本操作
结合上一篇同步异步,这篇理解线程操作. 1.新建线程.不止thread和runnable,Callable和Future了解一下 package com.thread; import java.tex ...
- Java高并发之同步异步
1.概念理解: 2.同步的解决方案: 1).基于代码 synchronized 关键字 修饰普通方法:作用于当前实例加锁,进入同步代码前要获得当前实例的锁. 修饰静态方法:作用于当前类对象加锁,进入同 ...
随机推荐
- vuex是什么东西?
vuex是什么鬼? 文档上面对vuex的解释是 "一个专为 Vue.js 应用程序开发的状态管理模式",恩,看完这句是否对vuex有了一个大概的认识? 答案是:"认识你个 ...
- JavaScript使用点滴
JavaScript使用点滴 一.字符串替换的小插曲 遇到一个小插曲,想要把后台返回的字符串输出给前端视图,字符串中包含\n换行,需要使用javascript对其进行替换成<br />. ...
- 关于 iOS 分类(Category)的实现代码
其实质是对一个类的扩充,对类扩充的方法有两种: (1)通过继承(经常用到) (2)通过分类 一个已知类Name 其头文件Name.h #import <Foundation/Foundation ...
- WinForm中ClickOnce发布至广域网
ClickOnce智能客户端,是微软提供比较早的一项技术,用于实现WinForm开发的应用程序能够自动更新,省去给每台客户端升级带来的困扰. 从网上的贴子里看,有的说好用,有的说不好用.客观的说,微软 ...
- django-站点管理
站点管理--超级用户的管理界面,可以让你添加,删除,管理网站内容: 一.激活管理界面 1.在settings.py中进行如下配置: INSTALLED_APPS = ( 'django.contrib ...
- 高通ASOC中的codec驱动
ASOC的出现是为了让codec独立于CPU,减少和CPU之间的耦合,这样同一个codec驱动就无需修改就可以匹配任何一款平台. 在Machine中已经知道,snd_soc_dai_link结构就指明 ...
- C#中await和async关键字的简单理解
C# 5.0之后,为了简化异步编程,引入了异步函数的概念,也就是方法标记async,然后可以使用await表达式来等待异步操作返回. await关键字看起来是一个阻塞线程的调用,但是实际上执行到awa ...
- Egret学习笔记 (Egret打飞机-4.添加主角飞机和实现飞行效果)
今天继续写点击了开始之后,添加一个飞机到场景中,然后这个飞机的尾巴还在冒火的那种感觉 先拆解一下步骤 1.首先完成飞机容器的图片加载 2.然后把容器添加到场景中 3.然后实现动画 -首先,我们新建一个 ...
- POJ - 1860 Bellman-Ford判正环
心累,陕西邀请赛学校不支持,可能要自费了.. 思路:套用Bellman-Ford判断负环的思路,把大于改成小于即可判定是否存在从源点能到达的正环.如果存在正环,那么完全多跑几次正环就可以把钱增加到足够 ...
- UVA1602
实现的细节很多,学到了如何翻转.旋转.平移,get很多技巧,值得一做. AC代码: #include<cstdio> #include<cstring> #include< ...