synchronized用于多线程访问,并且被修饰的部分不能同时被执行,是代码同步的一种方式。
1 使用synchronized修饰方法
1.1 synchronized修饰方法原理
过程:当多个线程同时访问被synchronized修饰的方法是,有且仅有一个线程可以被访问,当一个线程在访问时,其它线程只能等待。当一个线程访问完毕后,下一个线程才可以访问。
原理:当方法被synchronized修饰后,如果想要执行该方法就必须获得相应的锁。每个类有且仅有一个锁(针对静态方法),每个类的实例也是有且仅有一个锁。当多个线程在同时访问同一个方法时,执行该方法就必须获得相应的锁,同时锁只有一个,所以只能有一个线程可以获得锁,其它的线程必须等待该线程释放锁后才能获取到该锁。
进阶说明:由于每个类只有一个锁,所以当一个类中有多个方法被synchronized修饰时,在同一时间内只能有一个方法可以获得锁,所以只有一个被synchronized修饰的方法可以执行。
1.2 synchronized修饰方法示例
public void showDo(String msg){
for(int i=0;i<1000000;i++){
if (i%100000==0){
System.out.println("打印结果"+msg+i/100000);
}
}
}
//使用
new Thread(){
@Override
public void run() {
super.run();
showDo("线程一");
}
}.start();
new Thread(){
@Override
public void run() {
super.run();
showDo("线程二");
}
}.start();
结果:
image.png
未加synchronized修饰方法,可以看到执行顺序是打乱的,无序的。加了synchronized后:
public synchronized void showDo(String msg){
for(int i=0;i<1000000;i++){
if (i%100000==0){
System.out.println("打印结果"+msg+i/100000);
}
}
}
结果
image.png
2 使用synchronized修饰代码块
2.1使用synchronized修饰代码块说明
当使用synchronized在修饰代码块的时候需要一个自定义锁,当在多线程访问代码块的时候,只要获得自定义锁就可以执行。自定义锁可以是一个类,也可以是一个实例(可以是Object的子类,也可以是当前类自己),当具有相同自定义锁时代码块会顺序执行,当锁不同的时候互不影响。
2.2 使用synchronized修饰代码块示例
private static String s1 = "";
private static String s2 = "aa";
public void showDo(String msg) {
synchronized (s1){
for (int i = 0; i < 1000000; i++) {
if (i % 100000 == 0) {
System.out.println("打印结果" + msg + i / 100000);
}
}
}
}
public void showDo1(String msg) {
synchronized (s2){
for (int i = 0; i < 1000000; i++) {
if (i % 100000 == 0) {
System.out.println("打印" + msg + i / 100000);
}
}
}
}
//调用
new Thread() {
@Override
public void run() {
super.run();
showDo1("线程一");
}
}.start();
new Thread() {
@Override
public void run() {
super.run();
showDo("线程二");
showDo1("线程二");
}
}.start();
结果:
image.png
由上可得,多个同步锁,只有竞争同一个同步锁才会需要等待,不是竞争同一个锁的代码块互不影响。
synchronized不能修饰构造函数
定义接口方法时不能使用synchronized
synchronized(this)锁的是当前对象,当前有几个对象,this就有多少份
synchronized(XX.class)这个与当前对象无关,只要锁是XX.class的都会被同步
如果同一个类中有多个方法使用了同步锁synchronized(this)或者多个方法被synchronized修饰,则多个线程访问该类中同步方法时,每次只能访问一个,其它的被阻塞。如:
public synchronized void A(){
......
}
public synchronized void B(){
......
}
有两个线程分别访问同一个对象T的A方法和B方法,则同时只能有一个方法被其中一个线程访问,另一个线程处于阻塞状态,因为方法A和B持有同一个对象锁,synchronized(this)也是类似的情况。
同一个类中有多个方法使用了同步锁synchronized修饰,且这些类是静态的,因为静态方法是属于类的,而不是属于某个对象的,所以它与synchronized(XX.class)类似。
public synchronized static void method() {
// todo
}
每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。