!欢迎关注我的公众号:
![我的公众号](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20190606104746.png)
[toc]
# Java 中的 Synchronized 关键字
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
### 修饰一个代码块
一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞。
多个线程访问**不同对象**的时候,执行到这个代码块的时候,均会执行该方法,不会发生方法上锁的情况。
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20190610110614.png)
#### 多个线程访问同个对象的一个方法
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20190610134825.png)
结果:谁拿到锁谁执行,没拿到的等待
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20190610134911.png)
#### 多个线程访问多个对象的一个方法
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20190610134952.png)
结果:多个线程各自访问各自拥有对象的方法:
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20190610135040.png)
#### Synchronized 锁一个明确的对象
新建卖票类,sell 方法卖出一张票减一,addTicket 方法补一张票的库存。
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20190610140251.png)
新建卖票线程,在 run 方法里面每卖一张票就补一张票的库存。
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20190610140353.png)
最后开启五个卖票线程去卖票:
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20190610140504.png)
结果:
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20190610140542.png)
可以看到这里的并不是 从 A B C D E 依次执行的,因为谁拿到执行权就谁去执行。
#### 没有明确的锁,只是为了给一块代码加上同步
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20190610140832.png)
说明:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码
### 修饰一个方法
Synchronized 修饰一个方法的格式是
```java
public Synchronized void run(){
// code
}
```
Synchronized 关键字修饰的是整个函数里面的内容,当相同对象在多线程访问的 run 方法的时候,由于被 Synchronized 关键字修饰,不能同时访问。
这种写法等价于修饰代码块的这种写法:
```java
public void run(){
Synchronized(this){
// code
}
}
```
两种写法的 code 都是可以互斥访问的。
Synchronized 修饰方法的注意事项:
1. Synchronized 修饰方法不能被子类继承
2. 接口中不能写 Synchronized
3. 构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。
### 修饰一个静态的方法
这种情况也是针对于所有对象的这个方法,当多个线程访问这个代码块的时候,均会进行资源竞争的情况,谁拿到锁谁执行,没拿到的等待。
使用方法:
```java
public synchronized static void method() {
// code
}
```
Synchronized 作用在静态方法中,相当于给整个类加了锁,不管是类的哪个对象访问这个方法的时候,都会竞争锁的资源.
需要注意的是:对其他没加 Synchronized 的方法没有影响。
#### 加上 Synchronized 关键字
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20190610132600.png)
结果:
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20190610132650.png)
#### 不加 Synchronized
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20190610132727.png)
结果:
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20190610132752.png)
### 修饰一个类
这种情况也是针对于所有对象的这个方法,当多个线程访问这个代码块的时候,均会进行资源竞争的情况,谁拿到锁谁执行,没拿到的等待。
使用方法:
```java
class ClassName {
public void method() {
synchronized(ClassName.class) {
// todo
}
}
}
```
#### 使用 synchronized(ClassName.class)
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20190610133412.png)
结果:顺序执行,A 获得锁以后,B 只能等待 A 释放
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20190610133453.png)
#### 不使用 synchronized(ClassName.class)
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20190610133547.png)
结果:谁竞争到执行权,执行谁
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20190610133620.png)
## 总结:
1. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
2. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
3. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
- Java 面试题
- String、StringBuffer、StringBuilder 的区别?
- Java 中的四种引用
- 接口和抽象类的本质区别
- 集合框架
- 集合概述
- ArrayList 源码分析
- LinkedList 源码分析
- HashMap 源码分析
- LinkedHashMap 源码分析
- Android提供的 LruCache 的分析
- LinkedList 和 ArrayList 的区别
- 多线程
- 实现多线程的几种方式
- 线程的几种状态
- Thread 的 start() 和 run() 的区别
- sleep() 、yield() 和 wait() 的区别 ?
- notify() 和 notifyAll() 的区别?
- 保证线程安全的方式有哪几种?
- Synchronized 关键字
- volatile 和 synchronized 的区别?
- 如何正确的终止一个线程?
- ThreadLocal 原理分析
- 线程池
- 多线程的三个特征
- 五种线程池,四种拒绝策略,三种阻塞队列
- 给定三个线程如何顺序执行完以后在主线程拿到执行结果
- Java 内存模型
- 判定可回收对象算法
- equals 与 == 操作符
- 类加载机制
- 类加载简单例子
- 算法
- 时间、空间复杂度
- 冒泡排序
- 快速排序
- 链表反转
- IO
- 泛型
- Kolin 面试题
- Android 面试题
- Handler 线程间通信
- Message、MessageQueue、Looper、Handler 的对象关系
- Handler 使用
- Handler 源码分析
- HandlerThread
- AsyncTask
- IntentService
- 三方框架
- Rxjava
- rxjava 操作符有哪些
- 如何解决 RxJava 内存泄漏
- Rxjava 线程切换原理
- map和 flatmap 的区别
- Databinding引起的 java方法大于 65535 的问题
- Glide
- Glide 的缓存原理
- Glide 是如何和生命周期绑定的?不同的Context 有什么区别?
- Glide 、Picasso 、的区别,优劣势,如何选择?
- Jetpack
- 源码分析
- EventBus
- EventBus 源码分析
- RxBus 替代 EventBus
- OkHttp
- OkHttp 源码分析
- OkHttp 缓存分析
- RxPermission
- RxPermission 源码分析
- Retrofit
- create
- Retrofit 源码分析
- 优化
- 启动优化
- 布局优化
- 绘制优化
- 内存优化
- 屏幕适配
- 组件
- Activity
- Frgment
- Service
- ContentProvider
- BroadcastReceiver
- 进程间通信
- Binder机制和AIDL
- AILD 中的接口和普通的接口有什么区别
- in、out、inout 的区别
- Binder 为什么只需要拷贝一次
- 在android中,请简述jni的调用过程
- 生命周期
- Activity 生命周期
- Fragment 生命周期
- Service 生命周期
- onSaveInstanceState() 与 onRestoreIntanceState()
- 前沿技术
- 组件化
- 模块化
- 插件化
- 热更新
- UI - View
- Android 动画
- 事件分发机制
- WebView
- 系统相关
- 谈谈对 Context 的理解
- Android 版本
- App应用启动流程
- App 的打包
- App 的加固
- App 的安装
- Activity 启动流程
- ClassLoader
- Lru 算法加载 Bitmap 三级缓存原理
- Parcelable 和 Serializable 的区别
- Activity的启动流程
- 相关概念
- 网络相关
- Http
- Https
- Http 和 Https 的区别
- 为什么要进行三次握手和四次挥手?
- OkHttp使用Https访问服务器时信任所有证书
- 设计模式
- 单例模式
- 构建者模式
- 工厂模式
- 外观模式
- 代理模式