synchronized是一种同步锁
同一时刻只能有一个线程能获取到锁
- 修饰代码块:同步代码块,作用域是{}里面的代码,作用的对象是调用这个代码块的对象。
- 修饰方法:同步方法,作用范围是整个方法,作用对象是调用这个方法的对象。
- 修饰静态的方法:作用范围是整个方法,作用对象是这个类的所有对象。
- 修饰类:作用范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象。
1.synchronized 代码块1
2
3
4
5public void run() {
synchronized(obj) {
//一次只能有一个线程进入
}
}
synchronized锁住的是括号里的对象,不是代码。
当synchronized锁住一个对象时,别的线程也想拿到这个对象的锁,必须等待这个线程执行完释放锁,才能再次给这个对象加锁。
example
1 | public class SyncThread implements Runnable { |
1 | //1,2,3 |
1 | //3 |
1,3输出
- t1—:a begin
- t1—:a end
- t2—:a begin
- t2—:a end
2输出
- t2—:a begin
- t1—:a begin
- t1—:a end
- t2—:a end
1是对类的当前实例加锁
2是对锁特定的实例加锁
3是对该类的所有对象都加了锁,该类所有的对象同一把锁。
1.synchronized 方法1
2
3
4
5
6public synchronized void syncAdd() {
//4
}
public static synchronized void syncAdd() {
//5
}
example
1 | public class SyncThread { |
main
1 | public static void main(String[] arg){ |
输出
- funA-t1 : 4
- funA-t1 : 3
- funA-t1 : 2
- funA-t1 : 1
- funA-t1 : 0
- funB-t2 : 4
- funB-t2 : 3
- funB-t2 : 2
- funB-t2 : 1
- funB-t2 : 0
1 | public static void main(String[] arg){ |
- cSynct1 : 4
- cSynct1 : 3
- cSynct1 : 2
- cSynct1 : 1
- cSynct1 : 0
- cSynct2 : 4
- cSynct2 : 3
- cSynct2 : 2
- cSynct2 : 1
- cSynct2 : 0
4是对象锁
3,5得到的锁是类的锁
4是防止多线程同时访问这个对象的synchronized方法,(如果这个对象有多个synchronized方法,只要有一个线程访问了一个synchronized方法,其他的线程不能访问这个对象的任一synchronized方法),不同对象的synchronized方法互不影响。
5是防止多线程中不同实例对象同时访问方法,它对类的所有实例起作用。
synchronized修饰方法内对类变量i++,是线程安全吗?怎样保证安全?
由于i++;
操作并不具备原子性,该操作是先读取值,然后写回一个新值,相当于原来的值加上1,分两步完成 。
对于相同的实例是可以保证线程安全的。否则则不能保证。
使用Volatile,保证变量i的可见性,就可以保证线程安全。
1 | // |
synchronized的实现原理
Java 虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现,
无论是显式同步(有明确的 monitorenter 和 monitorexit 指令,即同步代码块)还是隐式同步都是如此。
在 Java 语言中,同步用的最多的地方可能是被 synchronized 修饰的同步方法。同步方法 并不是由 monitorenter 和 monitorexit 指令来实现同步的,而是由方法调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的。
参考