java并发编程之深入理解Synchronized的使用
作者:青苔小榭 发布时间:2023-10-11 09:21:13
1.为什么要使用synchronized
在并发编程中存在线程安全问题,主要原因有:1.存在共享数据 2.多线程共同操作共享数据。关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,同时synchronized可以保证一个线程的变化可见(可见性),即可以代替volatile。
2.实现原理
synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性
3.synchronized的三种应用方式
Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:
普通同步方法(实例方法),锁是当前实例对象 ,进入同步代码前要获得当前实例的锁
静态同步方法,锁是当前类的class对象 ,进入同步代码前要获得当前类对象的锁
同步方法块,锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
4.synchronized的作用
Synchronized是Java中解决并发问题的一种最常用最简单的方法 ,他可以确保线程互斥的访问同步代码
5.举栗子
**一、synchronized作用于实例方法**
①多个线程访问同一个对象的同一个方法
public class synchronizedTest implements Runnable {
//共享资源
static int i =0;
/**
* synchronized 修饰实例方法
*/
public synchronized void increase(){
i++;
}
@Override
public void run(){
for (int j =0 ; j<10000;j++){
increase();
}
}
public static void main(String[] args) throws InterruptedException {
synchronizedTest test = new synchronizedTest();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
结果:
分析:当两个线程同时对一个对象的一个方法进行操作,只有一个线程能够抢到锁。因为一个对象只有一把锁,一个线程获取了该对象的锁之后,其他线程无法获取该对象的锁,就不能访问该对象的其他synchronized实例方法,需要等到对象被释放后才能获取,但是在对象没有被释放前,其他线程可以访问非synchronized修饰的方法
②一个线程获取了该对象的锁之后,其他线程来访问其他synchronized实例方法现象 举栗
public class SynchronizedTest {
public synchronized void method1() {
System.out.println("Method 1 start");
try {
System.out.println("Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 1 end");
}
public synchronized void method2() {
System.out.println("Method 2 start");
try {
System.out.println("Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 2 end");
}
public static void main(String[] args) {
final SynchronizedTest test = new SynchronizedTest();
new Thread(test::method1).start();
new Thread(test::method2).start();
}
}
结果:
分析:可以看出其他线程来访问synchronized修饰的其他方法时需要等待线程1先把锁释放
③一个线程获取了该对象的锁之后,其他线程来访问其他非synchronized实例方法现象 举栗去掉②中方法二的synchronized
public class SynchronizedTest {
public synchronized void method1() {
System.out.println("Method 1 start");
try {
System.out.println("Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 1 end");
}
public void method2() {
System.out.println("Method 2 start");
try {
System.out.println("Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 2 end");
}
public static void main(String[] args) {
final SynchronizedTest test = new SynchronizedTest();
new Thread(test::method1).start();
new Thread(test::method2).start();
}
}
结果:
分析:当线程1还在执行时,线程2也执行了,所以当其他线程来访问非synchronized修饰的方法时是可以访问的
④当多个线程作用于不同的对象
public class SynchronizedTest {
public synchronized void method1() {
System.out.println("Method 1 start");
try {
System.out.println("Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 1 end");
}
public synchronized void method2() {
System.out.println("Method 2 start");
try {
System.out.println("Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 2 end");
}
public static void main(String[] args) {
final SynchronizedTest test1 = new SynchronizedTest();
final SynchronizedTest test2 = new SynchronizedTest();
new Thread(test1::method1).start();
new Thread(test2::method2).start();
}
}
结果:
分析:因为两个线程作用于不同的对象,获得的是不同的锁,所以互相并不影响
**此处思考一个问题:为什么分布式环境下synchronized失效?如何解决这种情况?
** **二、synchronized作用于静态方法**
public class synchronizedTest implements Runnable {
//共享资源
static int i =0;
/**
* synchronized 修饰实例方法
*/
public static synchronized void increase(){
i++;
}
@Override
public void run(){
for (int j =0 ; j<10000;j++){
increase();
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new synchronizedTest());
Thread t2 = new Thread(new synchronizedTest());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
结果:
分析:由例子可知,两个线程实例化两个不同的对象,但是访问的方法是静态的,两个线程发生了互斥(即一个线程访问,另一个线程只能等着),因为静态方法是依附于类而不是对象的,当synchronized修饰静态方法时,锁是class对象。
**三、synchronized作用于同步代码块**
为什么要同步代码块呢?在某些情况下,我们编写的方法体可能比较大,同时存在一些比较耗时的操作,而需要同步的代码又只有一小部分,如果直接对整个方法进行同步操作,可能会得不偿失,此时我们可以使用同步代码块的方式对需要同步的代码进行包裹,这样就无需对整个方法进行同步操作了。
public class synchronizedTest implements Runnable {
static synchronizedTest instance=new synchronizedTest();
static int i=0;
@Override
public void run() {
//省略其他耗时操作....
//使用同步代码块对变量i进行同步操作,锁对象为instance
synchronized(instance){
for(int j=0;j<10000;j++){
i++;
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(instance);
Thread t2=new Thread(instance);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
结果:
分析:将synchronized作用于一个给定的实例对象instance,即当前实例对象就是锁对象,每次当线程进入synchronized包裹的代码块时就会要求当前线程持有instance实例对象锁,如果当前有其他线程正持有该对象锁,那么新到的线程就必须等待,这样也就保证了每次只有一个线程执行i++;操作。当然除了instance作为对象外,我们还可以使用this对象(代表当前实例)或者当前类的class对象作为锁,如下代码:
//this,当前实例对象锁
synchronized(this){
for(int j=0;j<1000000;j++){
i++;
}
}
//class对象锁
synchronized(AccountingSync.class){
for(int j=0;j<1000000;j++){
i++;
}
}
来源:https://blog.csdn.net/zjy15203167987/article/details/82531772


猜你喜欢
- 前言每次update Maven Project 的时候,看着进度条寸步难行,心里憋得十分难受,明显阻碍我学习的热情。 maven仓库默认在
- 网上文章虽多,但是这种效果少之又少,我真诚的献上以供大家参考实现原理:自定义ImageView对此控件进行相应的layout(动态布局).这
- 静态库和动态库的区别1、静态库的扩展名一般为".a"或者".lib";动态库的扩展名一般为"
- 解决一个问题假如,程序需要向一个 Web 发送 5 次请求,受网路波动影响,有一定几率请求失败。如果失败了,就需要重试。示例代码如下:cla
- 一、对AOP的初印象首先先给出一段比较专业的术语(来自百度):在软件业,AOP为Aspect Oriented Programming的缩写
- telnet-client太费尽了,比ssh-client费尽的多,搞了一天,凑合能用,还得改。org.apache.commons.net
- Activity栈主要用于管理Activity的切换。当使用Intent跳转至某个目标Activity,需要根据目标Activity的加载模
- 添加依赖<dependency> <groupId>com.belerweb
- 本文主要介绍了Spring Security OAuth2 实现登录互踢的示例代码,分享给大家,具体如下:背景说明一个账号只能一处登录,类似
- 1、简介双重检查锁定(也叫做双重检查锁定优化)是一种软件设计模式。它的作用是减少延迟初始化在多线程环境下获取锁的次数,尤其是单例模式下比较突
- .NET Framework为动态列表List提供泛型类List<T>。这个类实现了IList,ICollection,IEnu
- eclipse运行tomcat提示端口被占用今天碰到一个问题,在eclipse中运行一个程序,eclipse里面的tomcat一启动就报错说
- public List<string> GetXYFromPic(String jpgPath) {
- 工作中有做过手机App项目,前端和android或ios程序员配合完成整个项目的开发,开发过程中与ios程序配合基本没什么问题,而andro
- 一.分类从实现的技术上来分类,目前主要有三种技术(或者说有三种产品):1.Java自带的java.util.Timer类,这个类允许你调度一
- 本文实例讲述了Spring使用ClassPathResource加载xml资源。分享给大家供大家参考,具体如下:一 代码package le
- 一、背景新做了一个的需求,需要在SpringBoot项目中引入了多个依赖,然后就感觉idea下载依赖包的时间很漫长,然后我就网上找了解决办法
- 本文主要关注如何使用mybatis/mybatis plus连接SQL Server数据库,因此将省略其他项目配置、代码。框架选择应用框架:
- 移动端微信消息页实现在上一篇中主界面实现说过微信四个页面中间都是是fragment的,并且四个fragment的布局都还没实现,所以这一篇主
- SpringAOP 通过JoinPoint获取参数名和值在Java8之前,代码编译为class文件后,方法参数的类型固定,但是方法名称会丢失