synchronized
是JAVA语言的一个关键字,使用synchronized
来修饰方法或代码块的时候,能够保证多个线程中最多只有一个线程执行该段代码 ...
<!-- more -->
概述
-
synchronized
关键字可以作为函数的修饰符,也可作为函数内的语句,也就同步方法
和同步代码块块
。细分为instance variable
(实例变量)、object reference
(对象引用)、static method
(静态方法) 和class literals
(常量类)。 - 无论·synchronized·关键字加在方法上还是对象上,它获取的都是
对象锁
,而不是将一段代码或一个函数当作锁,而且同步方法很可能还会被其他线程的对象访问。 - 每个对象只有一个锁(lock)与之相关联。
- 实现同步则是以系统开销作为代价,甚至可能造成死锁,所以尽量避免滥用。
同步方法: 使用 synchronized
标记的方法,只有获得该方法类实例的锁才能执行,否则所属线程将被阻塞,方法一旦执行,就独占该锁,直到该方法执行完毕将锁释放,被阻塞的线程才能获得锁从而执行。这种机制确保了同一时刻该类实例,所有声明为 synchronized
的函数中只有一个方法处于可执行状态,从而有效避免了类成员变量访问冲突。
同步方法缺陷:若将一个大的方法声明为 synchronized
将会大大的影响效率,典型的,若将线程类的方法 run() 声明为 synchronized
,由于在线程的整个生命期中它一直在运行,因此将导致对本类任何 synchronized
方法的调用都不会成功。因此在这种环境下,可以使用同步代码块
的方式
同步代码块: 除了方法前用synchronized
关键字,还可以用于方法中的某个区块中,表示只对该区域内的资源进行互斥操作。用法是: synchronized(this){/区块/}
,它的作用域是当前对象。也可以创建一个特殊的instance变量(它得是一个对象)
来充当锁
写法
类的范围写法,防止多个线程同时访问这个类中的synchronized method
,它可以对类的所有对象实例起作用
static synchronized void transferAccount() {
//...
}
//等同
static void transferAccount() {
synchronized(Bank.class) {
//...
}
}
对象实例内写法,多个线程同时访问该对象的synchronized 方法
,如果该对象实例有多个synchronized
方法,任意线程访问了其中的一个synchronized
方法,剩余线程则不能并发访问该对象中任何一个synchronized
方法(不同对象实例的synchronized
方法是互不干扰的。其它线程依然可以并发访问相同对象类不同实例
中的synchronized
方法,如果想做到在不同对象实例
同步需要使用class literal
的方式)
synchronized void transferAccount() {
//...
}
void transferAccount() {
synchronized(this) {
//...
}
}
private final byte[] LOCK = new byte[0]; // 特殊的实例化对象
void transferAccount() {
synchronized(LOCK) {
//...
}
}
案例一:静态同步方法
class Bank1 {
synchronized static void transferAccount() {
System.out.println("开始转账:" + Thread.currentThread().getName());
try {
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("转账完毕");
}
synchronized static void debit() {
System.out.println("开始扣款:" + Thread.currentThread().getName());
try {
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("扣款完毕");
}
}
public class BankMain {
public static void main(String[] args) {
new Thread(Bank1::transferAccount, "北京银行").start();
new Thread(Bank1::debit, "上海银行").start();
}
}
////////////////////////日志////////////////////////
开始转账:北京银行
转账完毕
开始扣款:上海银行
扣款完毕
////////////////////////日志////////////////////////
分析:通过日志看到在使用synchronized
后,虽然是调用的不同方法,但是线程还是同步去执行的(不加并发执行,结果你懂得(^▽^))
案例二:同步方法单一对象锁
class Bank2 implements Runnable {
@Override
public synchronized void run() {
System.out.println("查询数据:" + Thread.currentThread().getName());
System.out.println("开始转账:" + Thread.currentThread().getName());
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("转账完毕");
}
}
public class BankMain {
public static void main(String[] args) {
Bank2 bank2 = new Bank2();
new Thread(bank2, "北京银行").start();
new Thread(bank2, "上海银行").start();
}
}
////////////////////////日志////////////////////////
查询数据:北京银行
开始转账:北京银行
转账完毕
查询数据:上海银行
开始转账:上海银行
转账完毕
////////////////////////日志////////////////////////
分析:方法同步执行,谁获得锁谁先执行
案例三:Lock对象锁
class Bank3 implements Runnable {
private final byte[] LOCK = new byte[0]; // 特殊的实例化变量
@Override
public void run() {
System.out.println("查询数据:" + Thread.currentThread().getName());
synchronized (LOCK) {//该种方式只能锁
System.out.println("开始转账:" + Thread.currentThread().getName());
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("转账完毕");
}
}
}
public class BankMain {
public static void main(String[] args) {
Bank3 bank = new Bank3();
new Thread(bank, "北京银行").start();
new Thread(bank, "上海银行").start();
}
}
////////////////////////日志////////////////////////
查询数据:北京银行
查询数据:上海银行
开始转账:北京银行
转账完毕
开始转账:上海银行
转账完毕
////////////////////////日志////////////////////////
分析:互斥部分上锁,查询数据部分则并发执行
案例四:同步到多个对象锁
前文说过一个实例对象一把锁
,在案例三
与案例四
中,都只实例化了一个对象,当对象为多实例化的时候,需使用class literal
的方式,它和synchronized static method
方式产生的结果一样,取得的锁很特别,为当前调用该方法对象所属的类(而不再是由这个Class产生的某个具体对象了)。
class Bank4 implements Runnable {
@Override
public void run() {
System.out.println("查询数据:" + Thread.currentThread().getName());
synchronized (Bank4.class) {
System.out.println("开始转账:" + Thread.currentThread().getName());
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("转账完毕");
}
}
}
public class BankMain {
public static void main(String[] args) {
new Thread(new Bank4(), "北京银行").start();
new Thread(new Bank4(), "上海银行").start();
}
}
////////////////////////日志////////////////////////
查询数据:北京银行
查询数据:上海银行
开始转账:北京银行
转账完毕
开始转账:上海银行
转账完毕
////////////////////////日志////////////////////////
可以推断:如果一个类中定义了一个synchronized static methodA
,也定义了一个 synchronized 的 instance methodB
,该类同一个对象在多线程中分别访问A和B两个方法时,并不会构成同步,因为它们的锁都不一样。methodA
的锁是它的所属Class,而methodB
的锁是当前对象(该部分代码未贴出,可以自己实现或者看GIT
)
- 说点什么
- 个人QQ:1837307557
- battcn开源群(适合新手):391619659
微信公众号:battcn
(欢迎调戏)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。