1.背景
前几天线上项目出现一个问题,由于并发问题,导致服务器集群中的部分服务器中的数据没有更新。经过review代码,发现没有进行数据的同步操作。最后使用synchronize解决了问题。解决问题后,在空余时间对synchronized的应用进行了研究。
2.synchronized介绍
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
3. 示例
3.1 synchronized修饰代码块
如果多个线程访问的是同一个实例对象,则会出现等待,如果多个线程访问的都是不同的实例对象,则不会出现等待
3.1.1 synchronized(this)
代码如下:
public class SyncBlock implementsRunnable{
private static Integercount;
publicSyncBlock() {
// TODO Auto-generated constructor stub
count = 0;
}
public intgetCount(){
returncount;
}
@Override
public voidrun() {
// TODO Auto-generated method stub
System.out.println("当前执行的程序:"+Thread.currentThread().getName());
synchronized(this) {
for (int i = 0; i < 5; i++) {
try{
System.out.println(Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static voidmain(String[] args){
SyncBlock syncBlock = newSyncBlock();
Thread th1 = new Thread(syncBlock,"第一集团军");
Thread th2 = new Thread(syncBlock,"第二集团军");
th1.start();
th2.start();
}
}
执行结果如下:
通过执行结果可以看出,同步锁机制生效。synchronized(this)使得两个线程同步等待。在main函数中增加三行代码
public static void main(String[] args){
SyncBlock syncBlock = new SyncBlock();
SyncBlock syncBlock3 = new SyncBlock();
Thread th1 = new Thread(syncBlock,"第一集团军");
Thread th2 = new Thread(syncBlock,"第二集团军");
Thread th3 = new Thread(syncBlock3,"第三集团军");
th1.start();
th2.start();
th3.start();
}
执行结果如下:
通过执行结果可以看出,线程1和线程2之间出现了等待,线程1执行完之后执行线程2,而线程3与线程1和2之间没有等待。这是因为线程1和线程2是监视的同一个SyncBlock对象syncBlock,而线程3监视的是独立的对象syncBlock3。也就是说,synchronized(this)对于线程3与线程1和2,锁的是不同对象,所以彼此之间无法同步。
3.1.2 静态变量synchronized(this.count)
锁的对象是整型的实例对象count,如果count对象值是固定不变的,则所有线程之间都会等待,如果count是变化的,如count++,则所有线程之间都不会等待。因为count++的结果会改变count的对象引用,所以
synchronized(count)锁定的是不同的实例对象,也就没有起到锁的作用。
修改上面的代码如下:
1 public voidrun() {2 //TODO Auto-generated method stub
3 System.out.println("当前执行的程序:"+Thread.currentThread().getName());4 synchronized(count) {5 for (int i = 0; i < 5; i++) {6 try{7 System.out.println(Thread.currentThread().getName() + ":" + (count++));8 Thread.sleep(100);9 } catch(InterruptedException e) {10 e.printStackTrace();11 }12 }13 }14 }
执行结果如下:
如果count的值不发生改变
1 public voidrun() {2 //TODO Auto-generated method stub
3 System.out.println("当前执行的程序:"+Thread.currentThread().getName());4 synchronized(count) {5 for (int i = 0; i < 5; i++) {6 try{7 System.out.println(Thread.currentThread().getName() + ":" +(count));8 Thread.sleep(100);9 } catch(InterruptedException e) {10 e.printStackTrace();11 }12 }13 }14 }
执行结果如下:由于count值不发生变化,count是对多有实例对象共享,所以所有线程多会出现等待。
3.1.3 synchronized(A.class)
A.class表示类对象。每个类都对应着一个这样的类对象,所有线程都会彼此间等待。
修改代码如下:
1 public voidrun() {2 //TODO Auto-generated method stub
3 System.out.println("当前执行的程序:"+Thread.currentThread().getName());4 synchronized(SyncBlock.class) {5 for (int i = 0; i < 5; i++) {6 try{7 System.out.println(Thread.currentThread().getName() + ":" + (count++));8 Thread.sleep(100);9 } catch(InterruptedException e) {10 e.printStackTrace();11 }12 }13 }14 }
执行结果如下:
3.2 synchronized非静态方法
1 synchronized public voidrun() {2 //TODO Auto-generated method stub
3 System.out.println("当前执行的程序:"+Thread.currentThread().getName());5 for (int i = 0; i < 5; i++) {6 try{7 System.out.println(Thread.currentThread().getName() + ":" + (count++));8 Thread.sleep(100);9 } catch(InterruptedException e) {10 e.printStackTrace();11 }12 }14 }
执行结果如下,与在方法在使用synchronized(this)相同。多个线程访问的是同一个实例对象,则会出现等待,如果多个线程访问的都是不同的实例对象,则不会出现等待
3.4 synchronized静态方法
静态方法属于类方法,不属于任一实例对象。为所有实例对象所共享。因此对于所有线程调用synchronized静态方法,彼此之间会出现等待。与synchronized(A.clsss)类似。只是作用范围不同。