《java并发编程之美》第二章(主打一个陪伴学习)

看书总结,《java并发编程之美》第二章。

2.1、java中的线程安全问题

线程安全问题是指多个线程同时读写一个共享资源并没有任何同步措施时,导致出现脏数据或者其它不可预见的结果的问题。最常见的是使用synchronized去进行同步。

2.2、java中的共享变量内存不可见问题

java的内存模型规定,将所有的变量都放到主存储中,当线程使用变量时,会把主内存里面的变量赋值到自己的工作空间或者中作内存(一级缓存),线程读写变量时操作的是自己工作内存中的变量,处理完之后将变量值更新所有CPU共享的缓存(二级缓存),然后更新到主内存。内存不可见问题,出现在多个线程同时对一个共享变量进行修改时,一个线程修改的值对其它线程不可见的问题。主要问题时出现在,当前线程修改了变量值之后,刷新了自己的线程内的一级缓存和共享的二级缓存和主内存,但是没有刷新其它线程中的一级缓存,所以其它线程在操作这个共享变量的时候,在自己的一级缓存就命中了,所以对于其它线程写入的值并不可见。

2.3、java中的synchronized关键字

线程执行代码在进入synchronized代码块前会自动获取内部锁,这时候如果其它线程来访问同步代码块就会被阻塞挂起。synchronized的内存语义,进入synchronized块的内存语义是把在synchronized块内使用到的变量从工作内存中清除,这样在synchronized块内使用到该变量时就会从主内存中获取。退出synchronized块的内存语义是把在synchronized块内对共享变量的修改刷新到主内存。但是要注意的是,synchronized关键字引起上下文的切换并带来线程调度开销。

2.4、java中的volatile关键字

当一个变量被声明成volatile时,线程在写入变量时会把值更新回主内存;当其它线程读取共享变量时,会从主内存重新获取最新值 。volatile的内存语义和synchronized有相似之处,写入volatile变量相当于退出synchronized代码块(把写入工作内存的变量值同步到主内存);读取volatile变量值就相当于进入synchronized代码块(清理本地内存变量值,在从主内存获取最新值)。

使用volatile关键字的场景:

a、写入变量值不依赖变量的当前值。如果时依赖当前值的话,将是获取——计算——写入三步操作,这三部操作不是原子性的,而volatile不保证原子性。

b、读写变量值时没有加锁。加了锁了就没有用volatile的必要了。

2.5、java指令集重排序

重排序是指,代码指令在没有依赖关系,多线程并发的情况下,执行顺序会重排序,出现非预期的解决的情况。使用volatile修饰即可以解决问题。

2.6、伪共享

它是指,一个cache内存行会存放多个变量,但是当多个线程去修改一个内存行里面的多个变量时,同时只能有一个线程操作缓存行,所以相比将每个变量放到一个缓存行里,性能有所下降。

地址连续的变量才有可能被放到同一个缓存行(如数组)。单个线程修改一个缓存行的数据,会充分利用程序运行的局部性原则,充分加速程序运行。但是多个线程下并发修改一个缓存行中的多个变量时就会竞争缓存行,从而降低了程序的运行内存。

避免伪共享的方法:

jdk8之前使用字节填充来解决该问题,jdk8提供了一个sun.misc.Contended注解来解决伪共享问题。

2.7、各种锁

乐观锁和悲观锁:

悲观锁:悲观锁是指数据被外界修改保持保守的态度,认为数据很容易被其它线程修改,所以在数据被处理之前就会先对数据进程加锁。悲观锁的实现往往是使用数据库使用的锁机制,在查询语句后加 for update。

乐观锁:乐观锁是指数据在一般情况下不会造成冲突,所以在访问记录前不会对它进行加排它锁,而是进行数据提交更新时,才会正式对数据冲突与否进行检验,一般是在表中添加version字段或者使用业务状态来实现的。

公平锁和非公平锁:

公平锁表示线程获取锁的顺序时按照线程请求的时间早晚来决定的,先请求的先获得锁。

非公平锁则是大家一起去抢夺锁,不管先后请求。

公平锁:ReentrantLock pairLock = new ReentrantLock(true);

非公平锁:ReentrantLock pairLock = new ReentrantLock(false);

独占锁和共享锁:

即锁只能单个线程持有还是能被多个线程持有。ReentrantLock就是独占锁,而ReadWriteLock读写锁是共享锁。

可重构入锁:

是指一个线程获取一个已经被自己获取的锁时,是否会被阻塞。

自旋锁:

我们知道,当一个线程获取锁失败后会切换到内核状态而被挂起,而获取到锁时有要将其切换到内核状态将线程唤醒。用户状态切换到内核状态这个开销时比较大的。自选锁是指,在线程获取锁失败时,在不放弃CPU使用权的情况下,多次尝试获取。本质是使用CPU时间片换取线程阻塞与调度的开销。

Java并发编程之美 文章被收录于专栏

本专栏是本人看书——《java并发编程之美》的总结,主要是为自己做一个记录,也欢迎大家评论区留言讨论交流呀~

全部评论
太棒啦,小玲姐姐(手动狗头)
点赞
送花
回复
分享
发布于 2023-07-19 12:10 广东

相关推荐

头像
05-14 12:29
安卓
点赞 评论 收藏
转发
点赞 3 评论
分享
牛客网
牛客企业服务