`synchronized`关键字可以实现线程同步机制,防止对共享资源的访问冲突。通常将共享的资源放在同步块或同步方法中。
>[info]使用同步机制控制的代码块一般用**临界区**来描述
[toc]
# 1. `synchronized`同步块(不推荐)
**1.`synchronized`同步块语法**
```
synchronized(Object) {
//...
}
```
* 其他线程必须等到`synchronized`这个锁开锁后才能进入里面获取资源
* Object:为任意的对象,每个对象都有**1**和**0**这两个标志位,一个线程A运行到同步块时会首先检查Object对象的标志位,如果发现为**0**状态,则表明同步块正在被其他线程占用,线程A进入就绪状态,直到其它线程释放同步块,这时标志位变为**1**,表明线程A可以进入同步块中享用资源了,这时标志位变为**0**,禁止其他线程进入同步块。
* 同步块的锁定较弱,通常不推荐使用。
**2. 火车站售票案例**
将上节`线程同步机制`中模拟火车站售票系统新增同步机制功能,就可以解决剩余票数不合常理的问题。
```
public class SynchronizedTest implements Runnable {
int num = 10;
@Override
public void run() {
try {
while (true) {
synchronized ("") { // 同步块,
if (num >= 0) {
// 假设线程A调用sleep进入了休眠状态,这时线程B可能进入运行状态
// 记住:这只是一种可能,因为线程B可能还没有进入到运行状态,就先被其它线程
// (也可能是线程A)夺取了进入同步块的机会。
Thread.sleep(100);
System.out.println("票数剩余:" + num--);
}
if (num < 0) {
System.out.println("对不起,票已售完!");
return; // 退出线程
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SynchronizedTest t = new SynchronizedTest();
// 四个线程共同访问对象t,共享变量 num
Thread tA = new Thread(t);
Thread tB = new Thread(t);
Thread tC = new Thread(t);
Thread tD = new Thread(t);
tA.start();
tB.start();
tC.start();
tD.start();
}
}
```
每次运行的结果都如下:
```
票数剩余:10
票数剩余:9
票数剩余:8
票数剩余:7
票数剩余:6
票数剩余:5
票数剩余:4
票数剩余:3
票数剩余:2
票数剩余:1
票数剩余:0
对不起,票已售完!
对不起,票已售完!
对不起,票已售完!
对不起,票已售完!
```
<br/>
# 2. `synchronized`同步方法(推荐)
**1. `synchronized`同步方法语法**
```
synchronized void doit() {
//..
}
```
将上面的案例的同步块改成同步方法得出的结果是一样的:
```
public class SynchronizedMethod implements Runnable {
int num = 10;
@Override
public void run() {
while (true) {
doit();
if (num < 0) {
System.out.println("对不起,票已售完!");
return; // 退出线程
}
}
}
// 同步方法,将线程共享的资源放入同步方法中
synchronized void doit() {
try {
if (num >= 0) {
// 假设线程A调用sleep进入了休眠状态,这时线程B可能进入运行状态
// 记住:这只是一种可能,因为线程B可能还没有进入到运行状态,就先被其它线程
// (也可能是线程A)夺取了进入同步块的机会。
Thread.sleep(100);
System.out.println("票数剩余:" + num--);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SynchronizedMethod t = new SynchronizedMethod();
// 四个线程共同访问对象t, 共享变量 num
Thread tA = new Thread(t);
Thread tB = new Thread(t);
Thread tC = new Thread(t);
Thread tD = new Thread(t);
tA.start();
tB.start();
tC.start();
tD.start();
}
}
```
每次运行的结果都如下:
```
票数剩余:10
票数剩余:9
票数剩余:8
票数剩余:7
票数剩余:6
票数剩余:5
票数剩余:4
票数剩余:3
票数剩余:2
票数剩余:1
票数剩余:0
对不起,票已售完!
对不起,票已售完!
对不起,票已售完!
对不起,票已售完!
```
- 网络通信
- 网络协议
- 端口和套接字
- TCP网络程序
- UDP网络程序
- 多线程聊天室
- 多线程
- 线程相关概念
- 线程实现方式
- 中断线程
- 线程生命周期
- 线程优先级
- 优先级规则
- 案例演示
- 线程同步机制
- 线程同步机制
- synchronized关键字
- ReentrantLock类
- Condition类
- 监视器概念
- volatile关键字
- final变量
- 死锁
- 线程局部变量
- 读/写锁
- 原子类
- 阻塞队列
- 工作规则
- 案例演示
- 常用阻塞队列
- 线程安全集合
- 高效的映射/集/队列
- 并发集视图
- 写数组的拷贝
- Arrays类的并行数组算法
- 同步包装器
- Callable与Future
- 执行器
- 线程池
- 预定执行
- 控制任务组
- Fork-Join框架
- 同步器
- 同步器
- 信号量
- CountDownLatch类
- CyclicBarrier类
- Exchanger类
- SynchronousQueue类
- 线程与Swing
- Swing与线程问题
- 两个原则
- Swing工作线程
- 单一线程规则
- 文件IO
- File类
- 文件输入输出
- ZIP压缩文件
- 集合
- 集合框架
- 集合接口
- 集合实现类
- 线程安全集合
- 集合算法
- 迭代器
- 集合排序
- JDBC
- JDBC是什么
- JDBC-ODBC桥
- JDBC驱动程序类型
- JDBC常用类与接口
- 数据库操作
- 连接数据库
- 增/删/改/查/预处理
- 事务
- 批处理
- commons-dbutils工具
- 安全问题
- Jedis
- 使用Jedis操作Redis数据库
- JSON转换
- 使用连接池
- 案例
- 单例破坏
- 单例定义
- 单例实现方式
- 懒汉式实现单例
- 饿汉式实现单例
- 单例破坏
- 类的单例破坏
- 枚举的单例破坏
- 克隆
- 克隆是什么
- 浅克隆
- 深克隆
- 注解
- 注解是什么
- 三大注解
- 内置注解
- 元注解
- 自定义注解
- NIO
- 相关概念
- BIO/NIO/AIO
- 多线程编程
- 线程同步
- 线程通信
- NIO
- NIO三大核心组件
- NIO网络编程
- NIO文件读写
- AIO
- Java8新特性
- Lambda表达式
- 方法引用
- 函数式接口
- 默认方法
- 什么是默认方法
- 默认方法语法格式
- 多个同名的默认方法问题
- 静态默认方法
- 默认方法实例
- Stream
- Stream是什么
- Stream示例
- Optional容器
- 新的日期时间API
- Base64
- SPI
- SPI是什么
- SPI与API的区别
- 常见场景
- 使用SPI需遵循的约定
- SPI使用步骤