对实现了Runnable
或者Callable
接口类,可以通过多线程执行同一实例的run
或call
方法,那么对于同一实例中的局部变量(非方法变量
)就会有多个线程进行更改或读取,这就会导致数据不一致,synchronized
(关键字
)可以解决多线程共享数据同步的问题
synchronized使用说明
作用范围
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
-
修饰一个代码块
:被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象 -
修饰一个非静态方法
:被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象 -
修改一个静态的方法
:其作用的范围是整个静态方法,作用的对象是这个类的所有对象 -
修改一个类
:其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象
高能提示:
No1 > synchronized修饰的非静态方法:
如果一个对象
有多个synchronized方法
,只要一个线程
访问了其中的一个synchronized方法
,则这个线程所属对象
的其它线程
不能同时访问
这个对象
中任何一个synchronized方法
No2 > synchronized关键字是不能继承的:
基类的方法synchronized function(){}
在继承类中并不自动是synchronized function(){}
,而是变成了function(){}
。继承类需要你显式的指定
它的某个方法为synchronized方法
,可以通过子类调用父类的同步方法来实现同步
No3 > 针对synchronized修饰代码块和非静态方法,本质上锁的是代码块或非静态方法对应的对象
(代码块是synchronized标注的变量,非静态方法是所在类对应的实例
),如果是不同的对象
是可以同时访问的
No4 > 实现同步是要很大的系统开销
作为代价的,甚至可能造成死锁
,所以尽量避免无谓的同步控制
No5 > 每个对象只有一个锁(lock)与之相关联
No6 > 在定义接口方法
时不能使用synchronized
关键字
No7 > 构造方法不能使用synchronized关键字
,但可以使用synchronized代码块
来进行同步
1. 修饰一个代码块
public void syncCode(Object o) {synchronized (o) {// 同步代码块}
}
上面的锁就是o
这个对象,当然多个线程同步需要保证o
这个对象是同一个
,这是有明确的对象作为锁的情况,如果只是想单纯的让某一段代码同步,并没有明确的对象作为锁,可以创建一个特殊的instance
变量来充当锁
synchronized(o)
修饰的代码块,其中o
可以取值一个对象
或者一个变量
或者this
亦或者Clz.class
public class Sync implements Runnable {private byte[] lock = new byte[0];public void syncCode() {synchronized (lock) {// 同步代码块}}public void run ....
}
注
:零长度的byte数组
对象创建起来将比任何对象都经济,查看编译后的字节码
,生成零长度的byte[]
对象只需3条操作码,而Object lock = new Object()
则需要7行操作码
2. 修饰一个非静态方法
public synchronized void method() {// .....}
此时锁的是调用这个同步方法的对象
3. 修饰一个静态方法
public synchronized static void method() {// .....}
synchronized修饰的静态方法锁定的是这个类的所有对象
4. 修饰类
public class Sync implements Runnable {public void syncCode() {synchronized (Sync.class) {// 同步代码块}}public void run ....
}
和作用于静态方法一样,synchronized作用于一个类时,是给这个类加锁,类的所有对象用的是同一把锁
总结
- 线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。
- 线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他非同步方法
- 对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。
- 对于同步,要时刻清醒在哪个对象上同步,这是关键。
- 编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对"原子"操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。
- 当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
- 死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小,一旦程序发生死锁,程序将死掉