解决多线程安全问题的几种方式?
(1)同步代码块:
在代码块声明上 加上synchronized
synchronized (锁对象) {
可能会产生线程安全问题的代码
}
同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。
(2)同步方法:
在方法声明上加上synchronized
public synchronized void method(){
可能会产生线程安全问题的代码
}
同步方法中的锁对象是 this
静态同步方法: 在方法声明上加上static synchronized
静态同步方法中的锁对象是 类名.class
(3)同步锁
Lock接口提供了与synchronized关键字类似的同步功能,但需要在使用时手动获取锁和释放锁。
(4)锁类型
可重入锁:在执行对象中所有同步方法不用再次获得锁
可中断锁:在等待获取锁过程中可中断
公平锁: 按等待获取锁的线程的等待时间进行获取,等待时间长的具有优先获取锁权利
读写锁:对资源读取和写入的时候拆分为2部分处理,读的时候可以多线程一起读,写的时候必须同步地写
synchronized与Lock的区别
Lock详细介绍与Demo
1.lock中的主要方法
lock():获取锁,如果锁被暂用则一直等待
unlock():释放锁
tryLock(): 注意返回类型是boolean,如果获取锁的时候锁被占用就返回false,否则返回true
tryLock(long time, TimeUnit unit):比起tryLock()就是给了一个时间期限,保证等待参数时间
lockInterruptibly():用该锁的获得方式,如果线程在获取锁的阶段进入了等待,那么可以中断此线程,先去做别的事
通过 以上的解释,大致可以解释在上个部分中“锁类型(lockInterruptibly())”,“锁状态(tryLock())”等问题,还有就是前面子所获取的过程我所写的“大致就是可以尝试获得锁,线程可以不会一直等待”用了“可以”的原因。
Lock一般使用的例子,注意ReentrantLock是Lock接口的实现。
package com.brickworkers;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockTest {private Lock lock = new ReentrantLock();//需要参与同步的方法private void method(Thread thread){lock.lock();try {System.out.println("线程名"+thread.getName() + "获得了锁");}catch(Exception e){e.printStackTrace();} finally {System.out.println("线程名"+thread.getName() + "释放了锁");lock.unlock();}}public static void main(String[] args) {LockTest lockTest = new LockTest();//线程1Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {lockTest.method(Thread.currentThread());}}, "t1");Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {lockTest.method(Thread.currentThread());}}, "t2");t1.start();t2.start();}
}
//执行情况:线程名t1获得了锁
// 线程名t1释放了锁
// 线程名t2获得了锁
// 线程名t2释放了锁
tryLock():
package com.brickworkers;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockTest {private Lock lock = new ReentrantLock();//需要参与同步的方法private void method(Thread thread){/* lock.lock();try {System.out.println("线程名"+thread.getName() + "获得了锁");}catch(Exception e){e.printStackTrace();} finally {System.out.println("线程名"+thread.getName() + "释放了锁");lock.unlock();}*/if(lock.tryLock()){try {System.out.println("线程名"+thread.getName() + "获得了锁");}catch(Exception e){e.printStackTrace();} finally {System.out.println("线程名"+thread.getName() + "释放了锁");lock.unlock();}}else{System.out.println("我是"+Thread.currentThread().getName()+"有人占着锁,我就不要啦");}}public static void main(String[] args) {LockTest lockTest = new LockTest();//线程1Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {lockTest.method(Thread.currentThread());}}, "t1");Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {lockTest.method(Thread.currentThread());}}, "t2");t1.start();t2.start();}
}//执行结果: 线程名t2获得了锁
// 我是t1有人占着锁,我就不要啦
// 线程名t2释放了锁
Lock:Lock实现和synchronized不一样,后者是一种悲观锁,它胆子很小,它很怕有人和它抢吃的,所以它每次吃东西前都把自己关起来。而Lock呢底层其实是CAS乐观锁的体现,它无所谓,别人抢了它吃的,它重新去拿吃的就好啦,所以它很乐观。具体底层怎么实现,博主不在细述,有机会的话,我会对concurrent包下面的机制好好和大家说说,如果面试问起,你就说底层主要靠volatile和CAS操作实现的。
现在,才是我真正想在这篇博文后面加的,我要说的是:尽可能去使用synchronized而不要去使用LOCK
什么概念呢?我和大家打个比方:你叫jdk,你生了一个孩子叫synchronized,后来呢,你领养了一个孩子叫LOCK。起初,LOCK刚来到新家的时候,它很乖,很懂事,各个方面都表现的比synchronized好。你很开心,但是你内心深处又有一点淡淡的忧伤,你不希望你自己亲生的孩子竟然还不如一个领养的孩子乖巧。这个时候,你对亲生的孩子教育更加深刻了,你想证明,你的亲生孩子synchronized并不会比领养的孩子LOCK差。(博主只是打个比方)
那如何教育呢?
在jdk1.6~jdk1.7的时候,也就是synchronized16、7岁的时候,你作为爸爸,你给他优化了,具体优化在哪里呢:
1、线程自旋和适应性自旋
我们知道,java’线程其实是映射在内核之上的,线程的挂起和恢复会极大的影响开销。并且jdk官方人员发现,很多线程在等待锁的时候,在很短的一段时间就获得了锁,所以它们在线程等待的时候,并不需要把线程挂起,而是让他无目的的循环,一般设置10次。这样就避免了线程切换的开销,极大的提升了性能。
而适应性自旋,是赋予了自旋一种学习能力,它并不固定自旋10次一下。他可以根据它前面线程的自旋情况,从而调整它的自旋,甚至是不经过自旋而直接挂起。
2、锁消除
什么叫锁消除呢?就是把不必要的同步在编译阶段进行移除。
那么有的小伙伴又迷糊了,我自己写的代码我会不知道这里要不要加锁?我加了锁就是表示这边会有同步呀?
并不是这样,这里所说的锁消除并不一定指代是你写的代码的锁消除,我打一个比方:
在jdk1.5以前,我们的String字符串拼接操作其实底层是StringBuffer来实现的(这个大家可以用我前面介绍的方法,写一个简单的demo,然后查看class文件中的字节码指令就清楚了),而在jdk1.5之后,那么是用StringBuilder来拼接的。我们考虑前面的情况,比如如下代码:
String str1="qwe";
String str2="asd";
String str3=str1+str2;
底层实现会变成这样:
StringBuffer sb = new StringBuffer();
sb.append("qwe");
sb.append("asd");
我们知道,StringBuffer是一个线程安全的类,也就是说两个append方法都会同步,通过指针逃逸分析(就是变量不会外泄),我们发现在这段代码并不存在线程安全问题,这个时候就会把这个同步锁消除。
3、锁粗化
在用synchronized的时候,我们都讲究为了避免大开销,尽量同步代码块要小。那么为什么还要加粗呢?
我们继续以上面的字符串拼接为例,我们知道在这一段代码中,每一个append都需要同步一次,那么我可以把锁粗化到第一个append和最后一个append(这里不要去纠结前面的锁消除,我只是打个比方)
————————————————
版权声明:本文为CSDN博主「淳安郭富城」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
参考文章:https://blog.csdn.net/u012403290/article/details/64910926
解决多线程安全问题的几种方式?相关推荐
- 多线程实现的几种方式
多线程实现一共有四种方式,如下图: - pthread的使用 - 定义pthread typedef __darwin_pthread_t pthread_t; - 创建pthread int pth ...
- Linux C++多线程同步的四种方式
目录 一.互斥锁 二.条件变量 三.读写锁 原文链接:Linux C++多线程同步的四种方式(非常详细)_Y先森0.0-CSDN博客 背景问题:在特定的应用场景下,多线程不进行同步会造成什么问题? 通 ...
- SpringBoot解决跨域的5种方式
本文来说下SpringBoot中实现跨域的5种方式. 文章目录 什么是跨域 java解决CORS跨域请求的方式 返回新的CorsFilter(全局跨域) 重写WebMvcConfigurer(全局跨域 ...
- 解决多线程安全问题-无非两个方法synchronized和lock 具体原理以及如何 获取锁AQS算法 (百度-美团)
解决多线程安全问题-无非两个方法synchronized和lock 具体原理以及如何 获取锁AQS算法 (百度-美团) 参考文章: (1)解决多线程安全问题-无非两个方法synchronized和lo ...
- java几种多线程_Java多线程实现的几种方式
Java多线程实现的几种方式 多进程是计算机中的一个重要概念,通常一个任务称为一个进程,比如浏览网页.播放音乐都是一个进程. 在进程内部可能还需要执行多个子任务,比如在使用word文档打字的时候,不仅 ...
- java实现线程的方式_java多线程实现的四种方式
java多线程实现的四种方式1.继承Thread类,重写run方法(其实Thread类本身也实现了Runnable接口) 2.实现Runnable接口,重写run方法 3.实现Callable接口,重 ...
- java多线程的实现方式_JAVA多线程实现的三种方式
最近在做代码优化时学习和研究了下JAVA多线程的使用,看了菜鸟们的见解后做了下总结. 1.JAVA多线程实现方式 JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用E ...
- 什么是同源策略及解决跨域的三种方式
同源策略 1.1.1 所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个 ip 地址,也非同源.同源策略/SOP(Same origin policy)是一种约 ...
- 多线程实现的四种方式
Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callable接口通过FutureTask包装器来创建Thread线程.使用ExecutorService.Cal ...
最新文章
- 【Codeforces】1104C Grid game (变异的俄罗斯方块)
- Webbench的使用
- AtCoder ARC 090 E / AtCoder 3883: Avoiding Collision
- 理解SQLNET.AUTHENTICATION_SERVICES参数|转|
- CSS基础(part19)--CSS3属性选择器
- Android中WebView加载本地Html,与JavaScript与Android方法相互传值(续)...
- IP釋放、清除、以及刷新DNS
- spring知识点概述
- Pytorch-早停法(early stopping)原理及其代码
- 百度网盘mac损害计算机,百度网盘Mac版和Mac同步盘有哪些区别?百度网盘Mac版常见问题解答...
- jmeter做秒杀活动测试
- 计算机的快速启动栏,电脑快速启动栏不见了
- python实现最小二乘拟合函数(选择三种不同基函数,基函数可改变)
- 韩顺平零基础循序渐进学Java——自学笔记
- 谷歌账号被封怎么办?谷歌账号解封申诉步骤请收好!
- lambada表达式总结
- ubuntu设置swap(交换内存)
- 测试面试题——三角形
- mysql的报错日志哪里看_mysql错误日志
- java之封装,继承,多态