Java中的synchronized

来源:互联网 发布:淘宝女包2016新款上市 编辑:程序博客网 时间:2024/05/18 02:29

在Java中synchronized可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码。

而synchronized底层是通过使用对象的监视器锁(monitor)来确保同一时刻只有一个线程执行被修饰的方法或者代码块。可以用锁和钥匙来解释,被synchronized修饰的方法或者代码块是一把锁,这把锁是归对象所有的,当一个线程需要执行这些方法或者代码块的时候,锁就被钥匙插上了,所以其他线程就不能执行这些方法或者代码块。(实际情况还要更加复杂,这里只是便于理解,实际上synchronized的锁是可重入的)

下面会用几个示例解释一下:

  1. synchronized修饰方法:

    1. synchronized修饰static方法:

      /** * 用于测试synchronized修饰static方法的线程类 */public class SynchronizedStaticMethodThread extends Thread{    private static synchronized void print(){        //输出调用的线程及调用时的时间戳        System.out.println(System.currentTimeMillis()+" Call staticSynchronizedMethod by "+Thread.currentThread().getName());        try {            //调用线程休眠5秒,锁竞争效果更加明显            Thread.sleep(5000);        } catch (InterruptedException e) {            e.printStackTrace();        }    }    @Override    public void run() {        print();    }}

      下面是测试用例代码:

      public static void main(String[] args) {    SynchronizedStaticMethodThread t1=new SynchronizedStaticMethodThread();    SynchronizedStaticMethodThread t2=new SynchronizedStaticMethodThread();    SynchronizedStaticMethodThread t3=new SynchronizedStaticMethodThread();    t1.start();    t2.start();    t3.start();}

      运行结果:

      1511100950366 Call staticSynchronizedMethod by Thread-01511100955368 Call staticSynchronizedMethod by Thread-21511100960368 Call staticSynchronizedMethod by Thread-1

      分析:

      从输出结果可以看出,t1、t2和t3三个线程在竞争同一把锁,而这个锁其实SynchronizedStaticMethodThread.class这个Class对象的监视器锁。这是因为每个类都只有一个Class对象,每个类的各个实例对象都共享同一个Class对象。

    2. synchronized修饰非static方法:

      /** * 用于测试synchronized修饰非static方法的线程类 */public class SynchronizedMethodThread extends Thread{    private synchronized void print(){        //输出调用的线程及调用时的时间戳        System.out.println(System.currentTimeMillis()+" Call staticSynchronizedMethod by "+Thread.currentThread().getName());        try {            //调用线程休眠5秒,锁竞争效果更加明显            Thread.sleep(5000);        } catch (InterruptedException e) {            e.printStackTrace();        }    }    @Override    public void run() {        print();    }}

      运行结果:

      1511183399297 Call staticSynchronizedMethod by Thread-01511183399297 Call staticSynchronizedMethod by Thread-1

      分析:从运行结果可以看出t1和t2两个线程没有发生锁竞争,这是因为它们上的锁不是同一个锁,即它们的锁分别是两个对象(这里的对象其实就是指this,等价于synchronized(this))所持有。

  2. synchronized修饰代码块:

    /** * 展示了synchronized修饰代码块的例子 * @author RJH  * @date 2017年11月22日 下午8:21:56 */public class SynchronizedObjectDemo {    /**     * 类变量     */    private static Object staticObj=new Object();    /**     * 成员变量     */    private Object o=new Object();    /**     * 在类变量上锁的方法     */    public void printByStaticObj(){        synchronized (staticObj) {//在类变量上锁            try {                //调用线程休眠5秒,锁竞争效果更加明显                Thread.sleep(5000);            } catch (InterruptedException e) {                e.printStackTrace();            }finally{                //输出表示被调用                System.out.println(System.currentTimeMillis()+" printByStaticObj is called");            }        }    }    /**     * 在成员变量上锁的方法     */    public void printByObj(){        synchronized (o) {//在成员变量上错            try {                //调用线程休眠5秒,锁竞争效果更加明显                Thread.sleep(5000);            } catch (InterruptedException e) {                e.printStackTrace();            }finally{                //输出表示被调用                System.out.println(System.currentTimeMillis()+" printByObj is called");            }        }    }    public static void main(String[] args) {        //定义一个线程组        ThreadGroup threadGroup=new ThreadGroup(Thread.currentThread().getThreadGroup(),"SynchronizedObject");        for(int i=0;i<5;i++){//循环5次,每次都启动一个线程            SynchronizedObjectDemo objDemo=new SynchronizedObjectDemo();//每次都构造一个新对象            Thread printByObjThread=new Thread(threadGroup,new Runnable() {//构造新线程的时候把它加入到定义的线程组                @Override                public void run() {                    objDemo.printByObj();                }            });            printByObjThread.start();        }        while(true){            if(threadGroup.activeCount()==0){//死循环,直到定义的线程组中的线程都终止才执行后续步骤                for(int i=0;i<5;i++){                    //这里是调用类变量上锁的方法                    SynchronizedObjectDemo demo=new SynchronizedObjectDemo();                    Thread printByStaticObjThread=new Thread(new Runnable() {                    @Override                    public void run() {                        demo.printByStaticObj();                    }                    });                    printByStaticObjThread.start();                }                break;            }        }    }}

    输出结果如下:

    1511354086299 printByObj is called1511354086299 printByObj is called1511354086299 printByObj is called1511354086299 printByObj is called1511354086299 printByObj is called1511354091301 printByStaticObj is called1511354096302 printByStaticObj is called1511354101302 printByStaticObj is called1511354106302 printByStaticObj is called1511354111302 printByStaticObj is called

    分析:

    从这个输出结果可以看出,其实对于一个类变量加锁就类似给静态方法加锁,而对于一个成员变量加锁就类似给非静态方法加锁。

    这里需要注意的是,如果把第一次for循环中的SynchronizedObjectDemo对象的构造放到第一次for循环之前的话,输出的结果就会类似第二次for循环了,因为此时锁的是同一个对象。

总结:其实网上说的synchronized的类锁和对象锁都是针对对象进行锁,只是Class对象或者类变量只有一个。

原创粉丝点击