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 implements Runnable{
private static Integer count; public SyncBlock() { // TODO Auto-generated constructor stub count = 0; } public int getCount(){ return count; } @Override public void run() { // 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 void main(String[] args){ SyncBlock syncBlock = new SyncBlock(); 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 void run() { 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 void run() { 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 void run() { 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 void run() { 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)类似。只是作用范围不同。