java之同步锁---synchronized

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)类似。只是作用范围不同。

posted @ 2017-10-28 21:56  DaggerLee  阅读(3918)  评论(0编辑  收藏  举报