penglongli / blog

18 stars 1 forks source link

Java 的 synchronized 关键字 #114

Open penglongli opened 6 years ago

penglongli commented 6 years ago

synchronized 是 Java 用来同步的关键字,它修饰的类型如下:

目前其性能虽然得到了提升,但是仍然不建议使用

synchroized 使用

修饰代码块

public class Test {

    static class InnerClass {
        public void execute() {
            synchronized (this) {
                System.out.println(Thread.currentThread().getName() + " get lock");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    static class MyRunnable implements Runnable {

        InnerClass innerClass;

        MyRunnable(InnerClass innerClass) {
            this.innerClass = innerClass;
        }

        @Override
        public void run() {
            innerClass.execute();
        }
    }

    public static void main(String[] args) {
        InnerClass innerClass = new InnerClass();

        Thread t1 = new Thread(new MyRunnable(innerClass));
        Thread t2 = new Thread(new MyRunnable(innerClass));
        t1.start();
        t2.start();
    }
}

输出:

Thread-0 get lock
// 等待了 2s
Thread-1 get lock

修饰方法

public class Test {

    static class InnerClass {
        public synchronized void execute() {
            System.out.println(Thread.currentThread().getName() + " get lock");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static class MyRunnable implements Runnable {

        InnerClass innerClass;

        MyRunnable(InnerClass innerClass) {
            this.innerClass = innerClass;
        }

        @Override
        public void run() {
            innerClass.execute();
        }
    }

    public static void main(String[] args) {
        InnerClass innerClass = new InnerClass();

        Thread t1 = new Thread(new MyRunnable(innerClass));
        Thread t2 = new Thread(new MyRunnable(innerClass));
        t1.start();
        t2.start();
    }
}

输出:

Thread-0 get lock
// 等待了 2s
Thread-1 get lock

如果上述两个线程使用的 InnerClass 对象不同,则不会等待 2s,两个线程也不会出现阻塞同步的情况。

修饰静态方法

public class Test {

    static class InnerClass {
        public synchronized static void execute() {
            System.out.println(Thread.currentThread().getName() + " get lock");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static class MyRunnable implements Runnable {

        @Override
        public void run() {
            InnerClass.execute();
        }
    }

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

输出:

Thread-0 get lock
// 等待了 2s
Thread-1 get lock

修饰静态方法时候,拿到的是基于类的锁

修饰类

public class Test {

    static class MyRunnable implements Runnable {

        @Override
        public void run() {
            synchronized (MyRunnable.class) {
                System.out.println(Thread.currentThread().getName() + " get lock");

                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

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

输出:

Thread-0 get lock
// 等待了 2s
Thread-1 get lock

由于我们把同步关键字加到了 MyRunnable 类上,同一时刻只能由一个 Thread 来获得同步锁,另一个线程发生阻塞

修饰 final 对象

public class Test {

    static final InnerClass innerClass = new InnerClass();

    static class InnerClass {
        public void execute() {
            synchronized (innerClass) {
                System.out.println(Thread.currentThread().getName() + " get lock");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    static class MyRunnable implements Runnable {

        InnerClass innerClass;

        MyRunnable(InnerClass innerClass) {
            this.innerClass = innerClass;
        }

        @Override
        public void run() {
            innerClass.execute();
        }
    }

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

输出:

Thread-0 get lock
// 等待 2s
Thread-2 get lock
// 等待 2s
Thread-1 get lock

为什么只能是修饰 final 对象?

这句话说得很好:

if the object reference changes, the same section of code may be run in parallel
如果对象引用改变,相同的代码可能会出现并行执行的问题

我们的 synchronized 是对对象的引用加锁,如果对象引用改变了,此处的同步就没什么意义了。

如下:

ublic class Test2 {

    static InnerClass innerClass = new InnerClass();

    static class InnerClass {
        public void execute() {
            synchronized (innerClass) {
                System.out.println(Thread.currentThread().getName() + " get lock");
                innerClass = new InnerClass();
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    static class MyRunnable implements Runnable {

        InnerClass innerClass;

        MyRunnable(InnerClass innerClass) {
            this.innerClass = innerClass;
        }

        @Override
        public void run() {
            innerClass.execute();
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(new MyRunnable(innerClass));
        Thread t2 = new Thread(new MyRunnable(innerClass));
        Thread t3 = new Thread(new MyRunnable(innerClass));
        Thread t4 = new Thread(new MyRunnable(innerClass));
        Thread t5 = new Thread(new MyRunnable(innerClass));
        Thread t6 = new Thread(new MyRunnable(innerClass));
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
    }
}

上述的几个线程会出现并行执行的情况。

wait 与 notify

public class Test {

    static final Message message = new Message();

    static class Message {
        String msg;

        void setMsg(String msg) {
            System.out.println(msg);
            this.msg = msg;
        }
    }

    static class PassengerRunnable implements Runnable {

        @Override
        public void run() {
            synchronized (message) {
                try {
                    message.setMsg("乘客:司机,我位置准确,请来接我");
                    message.notify();

                    // 等待司机赶到定位地址
                    message.wait();

                    // 乘客赶往定位地址
                    Thread.sleep(2000);
                    System.out.println("乘客:好了,我也到了");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    static class DriverRunnable implements Runnable {

        @Override
        public void run() {
            synchronized (message) {
                try {
                    message.wait();
                    // 司机赶往目标地点
                    Thread.sleep(2000);

                    message.setMsg("司机:我已到达定位地址,速来");
                    message.notify();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        DriverRunnable driver = new DriverRunnable();
        PassengerRunnable passenger = new PassengerRunnable();

        new Thread(driver).start();
        Thread.sleep(1000);
        new Thread(passenger).start();
    }
}

上边是 wait 和 notify 的简单使用,场景并不太合适。