synchronized用法
synchronized可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码。
synchronized有三种应用方式:
- 作用于实例方法,当前实例加锁,进入同步代码前要获得当前实例的锁;
- 作用于静态方法,当前类加锁,进去同步代码前要获得当前类对象的锁;
- 作用于代码块,对括号里配置的对象加锁。
synchronized底层原理
1)同步方法
- 常量池中多了ACC_SYNCHRONIZED标识符,标识当前的方法是一个同步方法,当方法被调用,调用指令会检查方法ACC_SYNCHRONIZED标识符是否被设置,如果设置,执行线程会先去获取monitor,获取成功之后才会去执行方法题,方法体执行完成之后或释放 monitor。
2)同步代码块
- monitorenter
- 每一个对象都与一个monitor相关联,一个monitor的lock只能被一个线程在同一时间所
拥有,在一个线程尝试获取monitor的使用会发生以下事情:
- a.如果monitor的entry count为0,意味monitor的lock还没有被获取,某个线程获取
- 之后会对entry count+1,从此该线程就是这个monitor的所有者了
- b.如果一个已经拥有该monitor使用权的线程再次进入,会导致monitor的entry count+1
(可重入锁) - c.如果monitor已经被其他线程所拥有,其他线程尝试获取该monitor的额所有权时,会被陷入
- 阻塞,直到monitor的entry count为0,才能再次尝试去获取monitorexit
- 释放对monitor的使用权,要释放对某个对象的monitor的使用权前提是首先获取该monintor的
- 所有权,将monitor的entry count-1,如果entry count为0,那就表示该线程不再拥有该monitor 的使用
eg:
public class TestDemo7 {
//同步方法
//synchronzied修饰成员方法,synchornized获取this对象,this对象代表
//当前对象
public synchronized void test1(){
//获取test的monitor lock
// int i=5;
// while(i >=1){
// System.out.println(Thread.currentThread().getName()+"::"+i--);
// try {
// TimeUnit.MILLISECONDS.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
//将test的monitor lock释放
}
public void test2(){
//同步代码块
synchronized (this){
//获取test的monitor lock
// int i=5;
// while(i >=1){
// System.out.println(Thread.currentThread().getName()+"::"+i--);
// try {
// TimeUnit.MILLISECONDS.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
//test的monitor lock释放
}
}
public static void main(String[] args) {
Object obj = new Object();
//验证可重入锁
new Thread(){
@Override
public void run() {
synchronized (obj){
System.out.println("entry + 1");
synchronized (obj){
System.out.println("entry + 1 again");
}
}
}
}.start();
// TestDemo7 test = new TestDemo7();
// TestDemo7 test0 = new TestDemo7();
// new Thread("A"){
// @Override
// public void run() {
// //test.test1();
// test.test2();
// }
// }.start();
//
// new Thread("B"){
// @Override
// public void run() {
// //test.test1();
// test.test2();
// }
// }.start();
}
}
synchornized的使用场景
- 两个线程同时访问同一个对象的同步方法 安全
- 两个线程同时访问两个对象的同步方法 不安全 test1.func1() test2.func1()
- 两个线程同时访问(一个或两个)对象的静态同步方法 安全
- 两个线程分别同时访问(一个或两个)对象的同步方法和非同步方法 不安全
- 两个线程访问同一个对象中的同步方法,同步方法又调用另外一个非同步方法 不安全
- 两个线程同时访问同一个对象的不同的同步方法 安全
- 两个线程同时访问静态synchronized和非静态synchornized方法 不安全
- 同步方法抛出异常,JVM会自动释放锁
synchornized的练习
synchornized同步锁实现3个线程循环打印数字,使用线程1,打印1,2,3,4,5. 线程2,打印6,7,8,9,10.线程3,打印11,12,13,14,15.依次循环打印,直到 打印至60.
public class TestDemo8 {
public synchronized void fun1(){
System.out.println(Thread.currentThread().getName()+":: 同步方法fun1开始");
try {
TimeUnit.MILLISECONDS.sleep(1000);
// fun3();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":: 同步方法fun1结束");
}
public synchronized static void fun2() throws Exception {
//类锁 class对象
System.out.println(Thread.currentThread().getName()+":: 静态同步方法func2开始");
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":: 静态同步方法func2结束");
}
public void fun3(){
System.out.println(Thread.currentThread().getName()+":: 非同步方法func3");
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":: 非同步方法func3");
}
public synchronized void fun4(){
System.out.println(Thread.currentThread().getName()+":: 同步方法fun4开始");
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":: 同步方法func4结束");
}
public static void main(String[] args) {
TestDemo8 test1 = new TestDemo8();
TestDemo8 test2 = new TestDemo8();
new Thread("A"){
@Override
public void run() {
test1.fun1(); //unlock
test1.fun3();
}
}.start();
new Thread("B"){
@Override
public void run() {
test1.fun1(); //lock
test1.fun3();
}
}.start();