synchronized 的用法

synchronized 是java中的关键字,同步锁,用于解决多线程安全问题。 有以下几种用法

多线程安全问题的定义, 在 ConcurrentModificationException与多线程安全的关系 这篇文章末尾有 列举事例来论述, 简单来说就是:多线程在操作同一个变量时,如何保证一个线程的操作对其它线程立即可见。


synchronized 关键字就是用来解决 多线程安全 问题,  被 synchronized 关键字修饰的 代码块或 方法块,在同一时刻只允许 一个线程执行;当有任意一个线程正在执行 这段同步块时,其它任何访问 “this所代表的对象的线程” 都会被 阻塞,直到这段 同步代码块执行完成,其它被阻塞的线程(如果存在)会被立即唤醒;如果一个线程开始访问synchronized 修饰的代码块或方法体时,没有其它线程正在执行这段代码块或方法体,那么这个线程将立即获得 synchronized所修饰的代码块或方法体的锁,也就能正常执行这段代码。


synchronized 的 具体用法:

一. 修饰一个代码块, 被修饰的代码块称为同步语句块,其同步的作用范围是大括号{}括起来的代码块,锁住的对象由synchronized 后面的小括号里的内容决定。锁会有同步代码块执行完成时释放.

     小括号里的内容有以下几种场景

  a、synchronized(this) { 同步代码块 }     锁住的是this所指代的对象,当有任意一个线程正在执行 这段同步块时,其它任何访问 this所代表的对象的线程 都会被 阻塞,直到这段 同步代码块执行完成,因为执行完成时 之前锁住this对象的线程会释放锁,释放之后 ,其它线程才能操作this对象

  b、synchronized(new Object) { 同步代码块 }  

public class T{
    Object obj = new Object();
    void fun1(){
        synchronized(obj){
            //同步代码块
        }
    }
   
    void fun2(){
        //这个方法不会被阻塞住
    }
}

  同上锁住的对象是 obj对象,这样当某线程正在执行这段同步代码块时,其它线程只是会被obj阻塞,但是仍然可能访问 T类的对象的其它方法,如 fun2

  c、synchronized(T.class) { 同步代码块 }     锁住 T 的所有对象, 只要有任意一个线程在访问 T, 其它线程再访问时都会被阻塞住;   包括T的 所有实例对象的方法访问   和  访问T的静态成员或静态方法。

  这种方式效率比较低,在不需要锁住所有实例、所有方法的场景,不要使用这种方式。  在多线程资源竞争恶劣的情况,会大大降低多线程的效率。   

二、修饰方法;    同步的代码块就是这个方法体,锁住的对象是 略有差别, 锁会在方法体执行完成时释放

  a、 修饰非静态方法时,锁住的是正在执行这个同步方法的对象。

public synchronized void method()
{
   // todo
}
    b、修饰静态方法时, 锁住的是 这个类的所有对象。
public synchronized static void method() {
   // todo
}


synchronized可重入性:

重入性的定义:指的是同一个线程多次试图获取它所占有的锁,请求会成功。当释放锁的时候,直到重入次数清零,锁才释放完毕。

synchronized 锁是可重入的,并且synchronized锁也有计数器,在同一个线程内synchronized代码块中再次调用 被相同的锁锁住的synchronized代码块时,这个相同的线程能再次进入被锁住的代码块,锁的计数器再次自增1,达到2,所以要等到锁的计数器下降为0时才能释放锁,即需要内外两层synchronize代码块都执行完毕时,才会释放锁

Public class Widget {
      Public synchronized void doSomething(){
           …
      }
}


Public class LoggingWidget extends Widget {
   Public synchronized void doSomething(){
      System.out.println(toString()+”:calling doSomething”);
      Super.doSomething();
   }
}


总结:

A. 无论synchronized关键字加在方法上还是对象上,

如果它作用的对象是非静态的,则它取得的锁是对象;

如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象实例同一把锁。 

B. 每一段同步代码块,只能被锁上一次,哪个线程先执行到 synchronized 时,就先锁上这段同步代码块,直接这个线程执行完这段同步代码块时,才会解锁;这样其它被阻塞住的线程才会被唤起而继续执行这段同步代码块。

C. 每一个对象的同步代码块只有一个锁(lock)与之相关联,多个对象的同步代码块可以共用一把锁(如synch(TTT.class),则TTT的所有实例共用一个锁),  谁拿到这个锁谁就可以运行它所锁住的那段代码.

D. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。


疑问: 有空再来整一篇以提升理解

1、synchronized 同步代码块 执行完成时, 它是如何  唤醒 其它被阻塞住的 线程 继续执行这段 同步代码块的?  与 obj.wait、notiyf有什么关系?

2、除 synchronized 关键字,还有哪些手段能解决 多线程安全 的问题? 

猜你喜欢

转载自blog.csdn.net/u013394527/article/details/80552352