java并发编程笔记一

unsafesequence提供线程安全注解

类 Annotation(注解)

就像名字一样,这些注解是针对类的。主有要以下三个:

  • **@ThreadSafe **

是表示这个类是线程安全的。具体是否真安全,那要看实现者怎么实现的了,反正打上这个标签只是表示一下。不线程安全的类打上这个注解也没事儿。

  • @Immutable

表示类是不可变的,包含了 @ThreadSafe 的意思。

  • @NotThreadSafe

表示这个类不是线程安全的。如果是线程安全的非要打上这个注解,那也不会报错。

  • @GuardedBy( lock )

里面的Lock是告诉维护者:这个状态变量,这个方法被哪个锁保护着。这样可以强烈的提示类的维护者注意这里。
1、@GuardedBy( "this" ) 受对象内部锁保护
2、@GuardedBy( "fieldName" ) 受 与fieldName引用相关联的锁 保护。
3、@GuardedBy( "ClassName.fieldName" ) 受 一个类的静态field的锁 保存。
  4、@GuardedBy( "methodName()" ) 锁对象是 methodName() 方法的返值,受这个锁保护。
5、@GuardedBy( "ClassName.class" ) 受 ClassName类的直接锁对象保护。而不是这个类的某个实例的锁对象。
==这四个注解,对用户和维护者是有益的,用户可以立即看出来这个类是否是线程安全的,维护者则是可以根据这个注解,重点检查线程安全方面。另外,代码分析工具可能会利用这个注解。==

例子
import javax.annotation.concurrent.*;;  
  
@ThreadSafe  
public class Sequence {  
      
    @GuardedBy("this") private int value;  
      
    public synchronized int getNext() {  
        return value++;  
    }  
}  



线程的风险

活跃度风险

如果线程A等待线程B独立占有的资源,B永远不会释放,A将永远等待下去,各种形式活跃度失败,包括==死锁==、饥饿、活锁

性能危险

虽然线程能获取纯粹的性能收益,但线程会给程序带来一定程度的开销,上下文切换-当调度程序临时挂起当前运行程序时,另一个程序开始运行,这在多线程的应用程序中很频繁,并且带来巨大的系统开销**


线程安全性

当多个线程访问一个类是,如果不用考虑这些线程在运行时期的调度和交替执行,并且不需要额外的同步和调用方代码的协助,那么这个类是线程安全的

无状态和有状态对象的区别
  • 有状态就是有数据存储,有实例变量的对象,可以保存数据,是线程不安全的
public class A {  
   public A() {}  
   public String hello() {  
      return "Hello 谁?";  
   }  
}  
  
public class Client {  
   public Client() {  
      A a = new A();  
      System.out.println(a.hello());  
      A b = new A();  
      System.out.println(b.hello());  
   }  
}

在Client中生成了两个A的实例,不管是对象a还是b,它们是没有状态的。对于Client来说a和b是没有差别的(但a != b)。所以同一个无状态会话Bean的实例都是相同的,可以被不同的客户端重复使用。

  • 无状态就是不能保存数据,一次性操作,没有实例变量,不能保存数据,是线程安全,
public class B {  
      private String name;  
      public B(String arg) {  
      this.name = arg;  
      }  
      public String hello() {  
         return "Hello" + this.name;  
      }  
   }  
  
   public class Client {  
      public Client() {  
         B a = new B("中国");  
         System.out.println(a.hello());  
         B b = new B("世界");  
         System.out.println(b.hello());  
      }  
   } 

<font size=4>==无状态对象永远是线程安全的==</font>

把cotroller改为多例就一定线程安全吗,不一定,因为statci变量是多线程共享,也不一定能达成线程安全,一般来说spring容器的bean默认是单例,controller单例为的就是性能,不然每次请求创建一个实例,服务器不得消耗巨大。controller防止多线程问题就是避免在controller中写入变量和有状态对象,方法和局部变量是存储在栈内存中,而栈内存是线程私有的,因此只要不往controller中添加成员变量,都是线程安全

原子性

一套不可被分割的操作,一起成功要么一起失败

==例如:i++→实际包含三个操作==

  1. 获取当前i值
  2. i加1
  3. 写入新值

因此线程不安全

竞争条件

两个或多个对同一个资源进行操作的时候会产生竞争条件现象,主要是这个资源是非原子性,第一个线程没有结束操作,第二个线程就进来了,导致最后与预期结果不一致

保证同步

java中用lock或者synchronized来保证同步,但是这是悲观锁,会引起阻塞状态,CAS算法用来解决这个问题

CAS是一种无锁算法,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
==CAS不会造成阻塞状态==

java中用atomic类来替代多线程中成员变量的操作,atomic中用到的就是CAS算法,可以保证原子性


synchronized(独占锁)只有持有锁对象的线程能进入,其他的只能等待或阻塞,从而实现原子性

==但是响应速度太慢,不要用在方法上,最好用代码块实现==

可重入锁(ReentrantLock )-可实现公平锁

  • 可重入锁的概念就是一个线程可以连续多次获得锁资源,每次获取的时候内部有一个计数器stat 会加1,释放一个减去一,直到计数器为零,这个锁才可以背其他的线程获取
  • 通过加true实现公平锁
ReentrantLock fairLock = new ReentrantLock(true)

不可冲入锁造成的代码锁死:

public class widget{
    public synchronized void doSomething(){...}
}
public class LoggingWidget extends Widget{
    public synchronized void doSomething(){
        ....
        super.doSomething();
    }
}

总结:

冲入锁针对的是同一个线程的多次获取锁



共享对象

可见性

A线程修改共享数据data,B线程读取到了A线程修改之前的数据,访问了过期的数据

volatile变量

volatile变量,他确保对一个变量的更新以可预知的方式告知其他线程,编译器会监视这个变量,对它的操作也不会被其他的内存操作一起被重排序
==但是不支持原子性,所以适用于只有一个线程对其进行写操作,其他线程读==

重排序

什么是重排序?

执行任务的时候,为了提高编译器和处理器的执行性能,编译器和处理器(包括内存系统,内存在行为没有重排但是存储的时候是有变化的)会对指令重排序。编译器优化的重排序是在编译时期完成的,指令重排序和内存重排序是处理器重排序

  1. 编译器优化的重排序,在不改变单线程语义的情况下重新安排语句的执行顺序
  2. 指指令级并行重排序,处理器的指令级并行技术将多条指令重叠执行,如果不存在数据的依赖性将会改变语句对应机器指令的执行顺序
  3. 内存系统的重排序,因为使用了读写缓存区,使得看起来并不是顺序执行的
    ==会导致多线程可见性问题==

ThreadLocal维护线程

它允许你将每个线程与持有数值的对象关联在一起。ThreadLocal提供了get与set访问器,为每个适用它的线程维护一份单独的拷贝,通过get总是返回由当前执行线程通过set设置的最新值,从而实现了线程的封闭性

不可变性

不可变对象永远是线程安全的,可以安全的用于任意线程,甚至发布它们时亦不需要同步
只有满足如下状态,一个对象才是不可变的

  • 它的状态在创建后不能被修改
  • 所有的域都是final类型
  • 它被正确创建(创建期间没有发生this引用的溢出)


组合对象

委托线程线程安全

将对象中方法的线程安全委托出去,例如用currentHashmap来委托本来用synchonized修饰的添加方法,将线程安全委托到底层的状态变量

当委托无法胜任线程安全

调用了线程安全CurrentHashMap也不一定安全,因为方法运行中有判断,检查在运行,这套操作并非原子操作

image

如果类中含有复合操作,单独委托线程安全并不会达到安全的状态,比如在currentHashMap操作上加判断等操作,需要在这套操作上面加锁

同步容器

==只是容器中单个方法时线程安全,但多个方法结合在一起则需要调用者额外添加锁实现线程安全==

早期的jdk同步容器包括verctor和hashtable,都是由synchronized工厂方法创建,通过synchronized对每个公共方法进行同步

ConcurrentHashMap
  1. 分段锁:任意数量并发访问map,有限数量的写线程还可以并发修改map,优点:高吞吐量
CopyOnWriteArrayList

CopyOnWriteArrayList是同步List的并发替代品

Synchronizer(阻塞队列)

协调生产者线程和消费者线程之间的控制流

闭锁

闭锁是一种Synchronizer,可以延迟线程的进度知道线程到达终止状态。闭锁就像一道大门:知道闭锁到达终点状态之前,门一直是关闭的,没有线程可以通过,在终点状态到来之时,门开了所有线程都能通过

第一部分的总结:

  • 所有并发问题都归结为如何协调访问并发状态。可变状态越少,保证线程安全越容易
  • 尽量将域变量生命为final类型,除非他们的需求是可变的
  • 不可变对象天生是线程安全,可以在没有锁或者防御性复制的情况下自由共享
  • 用锁来守护每个可变变量
  • 对同一不变约束中的所有变量都使用相同的锁
  • 在运行复合操作期间需要持有锁
  • 在设计过程中就要考虑线程安全,或在文档中说明它不是线程安全的
  • 文档化你的同步策略
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342