synchronized用法

来源:互联网 发布:c语言graphics画虚线 编辑:程序博客网 时间:2024/05/16 19:18

参考地址:http://blog.csdn.net/luoweifu/article/details/46613015

synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:

<1> 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
<2>修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
<3>修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
<4>修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

1、对于普通方法同步,锁是当前实例对象
2、对于静态方法同步,锁是当前类的 Class 对象
3、对于方法块同步,锁是 Synchronized 括号里的对象

synchronized有两种用法

这里写图片描述
1、一种是写在方法前,如果该方法是静态方法,则获取的锁是类锁,多线程调用该类中所有的实例的该方法都是互斥的。如果该方法不是静态的,则获取的锁是对象锁,多线程调用同一个实例的该方法是互斥的,调用不同实例则不是互斥的。
2、一种是同步代码块:synchronized(){//….},同样根据方法是否是静态方法区分获取对象锁还是类锁。

wait:与synchronized一起使用,即必须想获取指定的锁lock,才能lock.wait。wait时,会释放lock,线程进入对象等待池,释放synchroined的锁,处于阻塞状态。当有其他线程notify,notifyAll后线 进入锁标识等待池,即进入同步阻塞状态。

notify:与synchronized一起使用,即必须想获取指定的锁lock,才能lock.notify,synchronized代码块结束后释放锁,通知一个因lock而进入等待阻塞的线程进入同步阻塞状态。

notifyAll:与synchronized一起使用,即必须想获取指定的锁lock,才能lock.notifyAll,synchronized代码块结束后释放锁,会通知所有因Lock而进入等待阻塞的线程进入同步阻塞状态。

sleep:线程暂停运行,进入阻塞状态,但不会释放之前持有的锁,sleep能够让低优先级的线程有机会运行。

yield:线程进入就绪状态,不会释放锁,让同优先级的其他线程有机会运行。但下次可能继续分配cpu时间片,进入运行状态。

join:让一个线程B加入到一个线程A的尾部,在A运行完前,B不能运行。

1、修饰代码块

public void method(){   synchronized(this) {       // this可以换成任意Object,即可以锁任意对象       //        // todo   }}

当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的对象来充当锁:

class Test implements Runnable{   private byte[] lock = new byte[0];  // 特殊的instance变量   public void method(){      synchronized(lock) {         // todo 同步代码块      }   }   public void run() {   }}

注:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。

2、修饰一个方法

public synchronized void method(){   // todo}

以上两个修饰方式是等价的,锁的是方法所在的类对应的实例

3、修饰一个静态的方法

public synchronized static void method() {   // todo}

我们知道静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象
同一个类的N个对象会共享static方法,并且共享一个锁。

4、修饰一个类

class ClassName {   public void method() {      synchronized(ClassName.class) {         //同步代码块      }   }}

synchronized作用于一个类T时,是给这个类T加锁,T的所有对象用的是同一把锁。
为了解决并发操作可能造成的异常,java的多线程支持引入了同步监视器来解决这个问题,使用同步监视器的通用方法就是同步代码块。其语法如下:

synchronized(obj){    //同步代码块}

其中obj就是同步监视器,它的含义是:线程开始执行同步代码块之前,必须先获得对同步监视器的锁定。任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行完成后,该线程会释放对该同步监视器的锁定。虽然java程序允许使用任何对象作为同步监视器,但是同步监视器的目的就是为了阻止两个线程对同一个共享资源进行并发访问,因此通常推荐使用可能被并发访问的共享资源充当同步监视器。

java线程存在以下几种状态

1、创建状态(New):线程被new出来,还未调用start
2、就绪状态(Runnable):又称为可执行状态,调用线程的start方法后,线程处于就绪状态,,线程调度程序还未给该线程分配cpu时间片执行。
3、运行状态(Running):线程调度程序分配cpu时间片来执行线程代码。
4、阻塞状态(Blocked):线程在运行过程中由于某种原因暂停运行进入阻塞状态,只有满足条件后进入就绪状态,获取cpu后才能再次进入运行状态。
阻塞的情况分三种:
A:等待阻塞(wait):调用wait()方法,与synchroined一起使用,线程进入对象等待池,释放synchroined的锁,处于阻塞状态。当有其他线程notify,notifyAll后线程进入锁标识等待池,即进入同步阻塞状态。
B:同步阻塞:线程运行过程中需要获取锁,但该锁被其他线程持有,则该线程进入锁标识等待池,处于同步阻塞状态。当线程获取锁之后,线程进入就绪状态。
C:其他阻塞:当线程sleep,或者join,或者发出I/O请求后,知道yield时间到,sleep时间到,join的线程执行完,或者I/O返回后,线程进入就绪状态。
5: 死亡状态(Dead):当线程Run方法退出或者运行出现异常线程停止时,线程就会消亡。

较复杂实例:

类SequentialDecoderUnion中的静态方法,private final static Map<Integer, Integer> name2col = new HashMap<>(); // 动态识别DDLprivate static void initialize( byte[] buf, int[] cols, int num ) {    synchronized (name2col) {         if (!dataTypeInitialized) {            // Initialization DDL. // 列名字到column 1是数字,2是字符串            if ('I' == buf[cols[4] + 1]) {                for (int j = 0, i = 6; i < num; i += 3, ++j) {                    int s0 = cols[i - 1] + 1;                     int s1 = findNextByte(buf, (byte) ':', s0);                    name2col.put(bytesHash(buf, s0, s1), j);                    col2type[j] = buf[s1 + 1];                }                colNumber = name2col.size();                dataTypeInitialized = true;                name2col.notifyAll();            } else {                do {                    try {                        name2col.wait();                    } catch (Exception ignored) {                    }                } while (!dataTypeInitialized);            }        }    }}

总结:

1、无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
2、每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
3、## 标题 ##实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。