synchronized关键字重新理解

我们都知道synchronized在java里是用来同步的,并且也比较常用。有两种用法,一是在方法上,二是在代码块上。对于在方法上,会造成同实例中别的同步方法阻塞么? 之前没想过这个问题,潜意识里觉得只是阻塞这个方法。

在看到 Object 的 wait 和 notify doc时 产生了疑问:

public final void wait ()

Causes the calling thread to wait until another thread calls the
notify() or notifyAll() method of this object. This method can only be
invoked by a thread which owns this object’s monitor; see notify() on
how a thread can become the owner of a monitor.

A waiting thread can be sent interrupt() to cause it to prematurely
stop waiting, so wait should be called in a loop to check that the
condition that has been waited for has been met before continuing.

While the thread waits, it gives up ownership of this object’s
monitor. When it is notified (or interrupted), it re-acquires the
monitor before it starts running.

public final void notify ()

Causes a thread which is waiting on this object’s monitor (by means of
calling one of the wait() methods) to be woken up. If more than one
thread is waiting, one of them is chosen at the discretion of the VM.
The chosen thread will not run immediately. The thread that called
notify() has to release the object’s monitor first. Also, the chosen
thread still has to compete against other threads that try to
synchronize on the same object.

This method can only be invoked by a thread which owns this object’s
monitor. A thread becomes owner of an object’s monitor

  • by executing a synchronized method of that object;
  • by executing thebody of a synchronized statement that synchronizes on the object;
  • by executing a synchronized static method if the object is of type Class.

理解下上面的话,假设有A,B两个线程, 对象为o。
A线程调用o.wait会阻塞A线程(要求调用时A线程必须要拥有该o的 monitor 才可以调用wait, 不然会报IllegalMonitorStateException异常,感兴趣的同学可以测一下);
当A线程处于wait状态阻塞时,A线程会放弃o的monitor。当B线程调用o.notify 时A最终会配唤醒,过程是:首先B线程会释放o的monitor, A竞争重新获得o 的 monitor。

怎样才能获得 o 的 monitor呢?
1. o 的 synchonized 方法
2. 同步 o 的synchronized代码块

到于文档中说的 static synchronized 对象是Class,只是一种特殊情况而已。

综上,object.wait, object.notify, synchronized , object’s monitor 四者是紧密相关的。

synchronized 是与 object’s monitor 有直接关系的,所以synchronized是针对object而言的。回到最初始的问题,synchronized方法 ,同步的也是这个对象,而不是方法。

验证如下:


class SyncWord {

    private synchronized void aa() {
        System.out.println(System.currentTimeMillis() + " aa");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private synchronized void bb() {
        System.out.println(System.currentTimeMillis() + " bb");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static synchronized void cc() {
        System.out.println(System.currentTimeMillis() + " cc");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    public static void main(String[] args) {
        SyncWord sw = new SyncWord();

        Thread t1 = new Thread(){
            public void run() {
                sw.aa();

            }
        };

        Thread t2 = new Thread(){
            public void run() {
                sw.bb();

            }
        };

        Thread t3 = new Thread(){
            public void run() {
                sw.cc();

            }
        };

        Thread t4 = new Thread(){
            public void run() {
                sw.cc();

            }
        };


        t1.start();
        t2.start();

//      t3.start();
//      t4.start();

    }
}

执行结果是:
1431072881631 aa
1431072886631 bb
间隔5s

**同实例两个不同synchronized方法,在不同线程里调用仍出现了同步关系。哪个线程拥有锁定了object(获得了object monitor), 那个线程就可以执行。
方法的synchronized 对应的object, 当然指得是方法所在的实例;
synchronized代码块的object, 指的是 synchronized(o) {...} 中的o;
对于 static synchronized 的object 就是 对应的 Class 对象。**


应用场景:
如果要求对象的synchronized所有方法都要求同步,就用方法synchronized;
反之,可以用代码块synchronized, 缩小同步范围,提高效率。

另外,同步对象 建议使用
private byte[] lock = new byte[0];
synchronized(lock) { //… }

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

参考资料: http://walsh.iteye.com/blog/336469

猜你喜欢

转载自blog.csdn.net/victor0535/article/details/45582187
今日推荐