(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

解决多线程安全问题的几种方式?相关推荐

  1. 多线程实现的几种方式

    多线程实现一共有四种方式,如下图: - pthread的使用 - 定义pthread typedef __darwin_pthread_t pthread_t; - 创建pthread int pth ...

  2. Linux C++多线程同步的四种方式

    目录 一.互斥锁 二.条件变量 三.读写锁 原文链接:Linux C++多线程同步的四种方式(非常详细)_Y先森0.0-CSDN博客 背景问题:在特定的应用场景下,多线程不进行同步会造成什么问题? 通 ...

  3. SpringBoot解决跨域的5种方式

    本文来说下SpringBoot中实现跨域的5种方式. 文章目录 什么是跨域 java解决CORS跨域请求的方式 返回新的CorsFilter(全局跨域) 重写WebMvcConfigurer(全局跨域 ...

  4. 解决多线程安全问题-无非两个方法synchronized和lock 具体原理以及如何 获取锁AQS算法 (百度-美团)

    解决多线程安全问题-无非两个方法synchronized和lock 具体原理以及如何 获取锁AQS算法 (百度-美团) 参考文章: (1)解决多线程安全问题-无非两个方法synchronized和lo ...

  5. java几种多线程_Java多线程实现的几种方式

    Java多线程实现的几种方式 多进程是计算机中的一个重要概念,通常一个任务称为一个进程,比如浏览网页.播放音乐都是一个进程. 在进程内部可能还需要执行多个子任务,比如在使用word文档打字的时候,不仅 ...

  6. java实现线程的方式_java多线程实现的四种方式

    java多线程实现的四种方式1.继承Thread类,重写run方法(其实Thread类本身也实现了Runnable接口) 2.实现Runnable接口,重写run方法 3.实现Callable接口,重 ...

  7. java多线程的实现方式_JAVA多线程实现的三种方式

    最近在做代码优化时学习和研究了下JAVA多线程的使用,看了菜鸟们的见解后做了下总结. 1.JAVA多线程实现方式 JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用E ...

  8. 什么是同源策略及解决跨域的三种方式

    同源策略 1.1.1 所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个 ip 地址,也非同源.同源策略/SOP(Same origin policy)是一种约 ...

  9. 多线程实现的四种方式

    Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callable接口通过FutureTask包装器来创建Thread线程.使用ExecutorService.Cal ...

最新文章

  1. 【Codeforces】1104C Grid game (变异的俄罗斯方块)
  2. Webbench的使用
  3. AtCoder ARC 090 E / AtCoder 3883: Avoiding Collision
  4. 理解SQLNET.AUTHENTICATION_SERVICES参数|转|
  5. CSS基础(part19)--CSS3属性选择器
  6. Android中WebView加载本地Html,与JavaScript与Android方法相互传值(续)...
  7. IP釋放、清除、以及刷新DNS
  8. spring知识点概述
  9. Pytorch-早停法(early stopping)原理及其代码
  10. 百度网盘mac损害计算机,百度网盘Mac版和Mac同步盘有哪些区别?百度网盘Mac版常见问题解答...
  11. jmeter做秒杀活动测试
  12. 计算机的快速启动栏,电脑快速启动栏不见了
  13. python实现最小二乘拟合函数(选择三种不同基函数,基函数可改变)
  14. 韩顺平零基础循序渐进学Java——自学笔记
  15. 谷歌账号被封怎么办?谷歌账号解封申诉步骤请收好!
  16. lambada表达式总结
  17. ubuntu设置swap(交换内存)
  18. 测试面试题——三角形
  19. mysql的报错日志哪里看_mysql错误日志
  20. java之封装,继承,多态

热门文章

  1. 恢复重装系统后的EFS加密文件
  2. 【调剂】北京语言大学 SAIT 智能语音习得技术实验室
  3. 单线激光雷达的外参标定方法
  4. Activiti7工作流介绍及使用
  5. FreeMaker一篇通
  6. LVGL | LVGL移植之中文文档
  7. vscode如何同时运行多个vue项目
  8. Android单元测试技巧
  9. Matlab 动态输入变量和嵌套函数、匿名函数
  10. Python PyQt5