synchronized,是java中多线程编程重要的内容,而在应用中多线程并发更是必不可少的,那么对于synchronized,我把这几天学习的内容记录下来。
synchronized是一种加锁同步机制,这就好比一栋房子只有一把钥匙,而只有拥有这把钥匙的人才能够进入,在出来之后,再将钥匙放置原处,等待下一个人来拿。在这种机制下,我们的多线程安全也就有了保证。
那么synchronized有几种用法,下面来看一看。
如果我们创建两个线程,分别调用test1和test2,下面来看结果。
可以看到,二者交替执行,并没有实现同步。出现这种情况的原因主要是因为普通方法和静态方法的引用的锁不是同一把。
我们把test1也改成static修饰的方法试试。
可以看到两个方法依次执行,说明我们同一个类中所有的以static修饰的方法,加上synchronized关键字,引用的是同一把class锁。
通过上面两个实验证明,synchronized关键字在修饰方法时
1,普通方法引用的是方法所在类的对象锁。
2,静态方法引用的是方法所在类的类锁。
问题:如果两个static方法不在同一个类中呢,会不会实现同步呢?请小伙伴们思考。
好了,说完了synchronized修饰方法之后,我们再来谈谈synchronized修饰代码块。
为什么会出现既可以修饰方法又可以修饰代码块的情况呢?
我们上面已经知道,不论是否static修饰的同步方法,都是引用的方法所在类的锁,那么如果该类中有很多synchronized修饰的方法,而恰巧此时进入的线程执行的操作时长过长或者是死循环,那么对于其他等待进入该类中的线程造成了长时间的等待,这对于系统是致命的,所以我们可以使用synchronized修饰代码块的方式来缩小同步的范围,最大化地提升系统的性能。这就好比我们在游乐场玩的滑行索道,为了保证安全,通常情况下是在前一个人到达地面的时候才允许后面的人开始向下滑行,而如果将索道从中间一分为二,那么前一个人在换到第二条索道时,后面的人就可以从第一条索道向下滑行,从而就减少了后面的人等待的时间。
那么synchronized修饰代码块应该注意什么呢。
来看结果
同样做到了同步,这其实刚才已经说过了,test1中引用的是类.class ,而test2虽然不能指定引用对象,但它其实同样引用了类.class 。
那如果我将上面例子中的SynchTest.class换成SynchTest.this。
那这样的话就不能实现同步了。
再如果我将上面例子test2的static修饰去掉呢?对,同样无法同步。
说到这里,想必明白一些了,其实可以总结一下几点:
1,synchronized修饰普通方法时,引用的是方法所在类的对象。
2,synchronized修饰static方法时,引用的是方法所在类的class。
3,synchronized修饰代码块时,括号中的对象一致时,则会形成互斥。
4,在使用synchronized时,尽量缩小其影响范围。
synchronized是一种加锁同步机制,这就好比一栋房子只有一把钥匙,而只有拥有这把钥匙的人才能够进入,在出来之后,再将钥匙放置原处,等待下一个人来拿。在这种机制下,我们的多线程安全也就有了保证。
那么synchronized有几种用法,下面来看一看。
1,synchronized修饰普通方法与静态方法。
public synchronized void test1(){
String name = Thread.currentThread().getName();
for (int i = 0; i < 4; i++) {
try {
Log.e("test1",name);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static synchronized void test2(){
String name = Thread.currentThread().getName();
for (int i = 0; i < 4; i++) {
try {
Log.e("test2",name);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
如果我们创建两个线程,分别调用test1和test2,下面来看结果。
test1: Thread-795
test2: Thread-796
test2: Thread-796
test1: Thread-795
test2: Thread-796
test2: Thread-796
test1: Thread-795
test1: Thread-795
可以看到,二者交替执行,并没有实现同步。出现这种情况的原因主要是因为普通方法和静态方法的引用的锁不是同一把。
我们把test1也改成static修饰的方法试试。
test1: Thread-801
test1: Thread-801
test1: Thread-801
test1: Thread-801
test2: Thread-802
test2: Thread-802
test2: Thread-802
test2: Thread-802
可以看到两个方法依次执行,说明我们同一个类中所有的以static修饰的方法,加上synchronized关键字,引用的是同一把class锁。
通过上面两个实验证明,synchronized关键字在修饰方法时
1,普通方法引用的是方法所在类的对象锁。
2,静态方法引用的是方法所在类的类锁。
问题:如果两个static方法不在同一个类中呢,会不会实现同步呢?请小伙伴们思考。
好了,说完了synchronized修饰方法之后,我们再来谈谈synchronized修饰代码块。
为什么会出现既可以修饰方法又可以修饰代码块的情况呢?
我们上面已经知道,不论是否static修饰的同步方法,都是引用的方法所在类的锁,那么如果该类中有很多synchronized修饰的方法,而恰巧此时进入的线程执行的操作时长过长或者是死循环,那么对于其他等待进入该类中的线程造成了长时间的等待,这对于系统是致命的,所以我们可以使用synchronized修饰代码块的方式来缩小同步的范围,最大化地提升系统的性能。这就好比我们在游乐场玩的滑行索道,为了保证安全,通常情况下是在前一个人到达地面的时候才允许后面的人开始向下滑行,而如果将索道从中间一分为二,那么前一个人在换到第二条索道时,后面的人就可以从第一条索道向下滑行,从而就减少了后面的人等待的时间。
那么synchronized修饰代码块应该注意什么呢。
我将test1循环打印的部分使用synchronized (SynchTest.class)修饰,注意:这里使用的关键字是所在类.class
public void test1(){
String name = Thread.currentThread().getName();
synchronized (SynchTest.class){
for (int i = 0; i < 4; i++) {
try {
Log.e("test1",name);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static synchronized void test2(){
String name = Thread.currentThread().getName();
for (int i = 0; i < 4; i++) {
try {
Log.e("test2",name);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
来看结果
test1: Thread-813
test1: Thread-813
test1: Thread-813
test1: Thread-813
test2: Thread-814
test2: Thread-814
test2: Thread-814
test2: Thread-814
同样做到了同步,这其实刚才已经说过了,test1中引用的是类.class ,而test2虽然不能指定引用对象,但它其实同样引用了类.class 。
那如果我将上面例子中的SynchTest.class换成SynchTest.this。
那这样的话就不能实现同步了。
再如果我将上面例子test2的static修饰去掉呢?对,同样无法同步。
说到这里,想必明白一些了,其实可以总结一下几点:
1,synchronized修饰普通方法时,引用的是方法所在类的对象。
2,synchronized修饰static方法时,引用的是方法所在类的class。
3,synchronized修饰代码块时,括号中的对象一致时,则会形成互斥。
4,在使用synchronized时,尽量缩小其影响范围。
以上就是我学习的内容,欢迎各位大神指正补充。