Java Synchronized 关键字详解

Reference:
https://www.imooc.com/video/20418
https://stackoverflow.com/questions/1085709/what-does-synchronized-mean

The synchronized keyword causes a thread to obtain a lock when entering the method,
so that only one thread can execute the method at the same time

synchronized method: acquires the lock of that object

synchronized statements/block :
1.Every object/class has an intrinsic lock associated with it.
2.When a thread invokes a synchronized statement, it automatically acquires the intrinsic lock for that synchronized statement's object and releases it when the method returns. As long as a thread owns an intrinsic lock, NO other thread can acquire the SAME lock => thread-safe.

面试常考

面试常考关于synchronized的7种情景
1.两个线程call同一个对象的同步方法?
只有一个线程先拿到锁进去,然后执行完,释放锁,第二个线程在拿锁再进去执行
2.两个线程call两个对象的同步方法。
因为锁是两个不同的实例,两个线程会同时执行/并行
3.两个线程call一个同步静态方法。
只有一个线程先拿到锁进去,然后执行完,释放锁,第二个线程在拿锁再进去执行
4.两个线程一个访问同步方法,另一个访问非同步方法。
两个线程会同时执行/并行,非同步方法不受影响
5。call一个对象的不同的同步方法,两个线程,一个call一个同步方法,另一个访问call一个同步方法。
只有一个线程先拿到锁进去,然后执行完,释放锁,第二个线程在拿锁再进去执行(锁是这个对象本身)
6。同时call静态的同步方法和非静态的同步方法。
一个锁是当前对象,一个是当前的Class对象,所以两个线程会同时执行
7。方法抛出异常后会不会释放锁?synchronized 会vs Lock 不会。
一个线程一旦抛出了异常,捕获了以后,会继续执行,执行完以后,然后释放锁,另外一个线程会开始执行


Screen Shot 2019-11-12 at 17.17.27.png

Synchronized 关键字的性质

同一个线程内,如果该线程已经有那把锁,可以用自己拥有的那把锁进入其他的需要那把锁的方法

Screen Shot 2019-11-12 at 17.27.18.png

Screen Shot 2019-11-12 at 17.33.08.png

Screen Shot 2019-11-12 at 17.53.56.png

Code example

If No Synchronized(each run return different i)

    public static void ifNoSynchronized() {
        i = 0; // reset
        Runnable r = () -> {
            for (int j = 0;j < 10000; j++) {
                i++; //contains three operation: read i, then i = i+1; write back to memory
            }
        };
        Thread t1 = new Thread( r );
        Thread t2 = new Thread( r );
        t1.start();
        t2.start();
        try {
            //https://stackoverflow.com/questions/15956231/what-does-this-thread-join-code-mean
            //t1 and t2 threads have been running in parallel
            //but the main thread that started them needs to wait for them to finish before it can continue
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // each runs print different i
        System.out.println("if no Synchronized:" + i);

    }

Then add Synchronized(then each time will always be 20000)

    public static void ifSynchronized() {
        i = 0; //reset
        // https://stackoverflow.com/questions/3369287/what-is-the-difference-between-synchronized-on-lockobject-and-using-this-as-the
        // lock with empty Object instead of this
        Object lock = new Object();
        Runnable r = () -> {
            //The synchronized keyword causes a thread to obtain a lock when entering the method,
            //so that only one thread can execute the method at the same time
            //synchronized method : acquires the lock of that object

            /*
            synchronized statements/block :
            Every object/class have an intrinsic lock associated with it.
            When a thread invokes a synchronized statement, it automatically acquires the intrinsic lock for that synchronized statement's object and releases it when the method returns. As long as a thread owns an intrinsic lock, NO other thread can acquire the SAME lock => thread safe.
             */
            synchronized (lock) {
                for (int j = 0;j < 10000; j++) {
                    i++; //contains three operation: read i, then i = i+1; write back to memory
                }
            }
        };
        Thread t1 = new Thread( r );
        Thread t2 = new Thread( r );
        t1.start();
        t2.start();
        try {
            //https://stackoverflow.com/questions/15956231/what-does-this-thread-join-code-mean
            //t1 and t2 threads have been running in parallel
            //but the main thread that started them needs to wait for them to finish before it can continue
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // each runs print different i
        System.out.println("if Synchronized:" + i);

    }

Lock on same & different object

    public static void ifSynchronizedOnSameLock() {
        Object lock = new Object();
        Runnable r = () -> {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + " will sleep");
                try {
                    Thread.sleep(2000);//during the sleep, lock was hold, so other thread for this lock can't execute
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " wake up");
            }


        };
        Thread t1 = new Thread( r );
        Thread t2 = new Thread( r );
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public static void ifSynchronizedOnDifferentLock() {

        Runnable r = () -> {
            // lock on different object, then two thread will execute at same time
            Object lock = new Object();
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + " will sleep");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " wake up");
            }


        };
        Thread t1 = new Thread( r );
        Thread t2 = new Thread( r );
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

两个线程一个执行某对象的一个synchronized方法,一个执行某对象的另外一个synchronized方法
执行结果:只有一个线程先拿到锁进去,然后执行完,释放锁,第二个线程在拿锁再进去执行(锁是这个对象本身)

然后遇到synchronized方法抛出异常的情况,异常捕获后,接着执行完当前synchronized方法,释放锁

    public static void sameObjectDifferentSynchronizedMethod() {
        SynchronizedTest st = new SynchronizedTest();
        Runnable r1 = () -> {
            st.test1();
        };
        Runnable r2 = () -> {
            st.test2();
        };
        Thread t1 = new Thread( r1 );
        Thread t2 = new Thread( r2 );
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //https://www.imooc.com/video/18614
    public static void synchronizedWithException() {
        SynchronizedTest st = new SynchronizedTest();
        Runnable r3 = () -> {
            st.test3();
        };
        Runnable r2 = () -> {
            st.test2();
        };
        Thread t1 = new Thread( r3 );
        Thread t2 = new Thread( r2 );
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void test1() {
        System.out.println(Thread.currentThread().getName() + " will sleep:test1");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " wake up:test1");
    }

    public synchronized void test2() {
        System.out.println(Thread.currentThread().getName() + " will sleep:test2");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " wake up:test2");
    }

    public synchronized void test3() {
        System.out.println(Thread.currentThread().getName() + " will sleep:test3");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        try {
            throw new Exception();
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + " running:test3");

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

        System.out.println(Thread.currentThread().getName() + " done: test3");
    }

Whole Source Code:


//https://www.imooc.com/video/20418
public class SynchronizedTest {
    static int i = 0;
    public static void main(String[] args) {
//        SynchronizedTest.ifNoSynchronized();
//        SynchronizedTest.ifSynchronized();
//        SynchronizedTest.ifSynchronizedOnSameLock();
//        SynchronizedTest.ifSynchronizedOnDifferentLock();
//        SynchronizedTest.sameObjectDifferentSynchronizedMethod();
          SynchronizedTest.synchronizedWithException();
    }

    public static void ifNoSynchronized() {
        i = 0; // reset
        Runnable r = () -> {
            for (int j = 0;j < 10000; j++) {
                i++; //contains three operation: read i, then i = i+1; write back to memory
            }
        };
        Thread t1 = new Thread( r );
        Thread t2 = new Thread( r );
        t1.start();
        t2.start();
        try {
            //https://stackoverflow.com/questions/15956231/what-does-this-thread-join-code-mean
            //t1 and t2 threads have been running in parallel
            //but the main thread that started them needs to wait for them to finish before it can continue
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // each runs print different i
        System.out.println("if no Synchronized:" + i);

    }

    public static void ifSynchronized() {
        i = 0; //reset
        // https://stackoverflow.com/questions/3369287/what-is-the-difference-between-synchronized-on-lockobject-and-using-this-as-the
        // lock with empty Object instead of this
        Object lock = new Object();
        Runnable r = () -> {
            //The synchronized keyword causes a thread to obtain a lock when entering the method,
            //so that only one thread can execute the method at the same time
            //synchronized method : acquires the lock of that object

            /*
            synchronized statements/block :
            Every object/class have an intrinsic lock associated with it.
            When a thread invokes a synchronized statement, it automatically acquires the intrinsic lock for that synchronized statement's object and releases it when the method returns. As long as a thread owns an intrinsic lock, NO other thread can acquire the SAME lock => thread safe.
             */
            synchronized (lock) {
                for (int j = 0;j < 10000; j++) {
                    i++; //contains three operation: read i, then i = i+1; write back to memory
                }
            }
        };
        Thread t1 = new Thread( r );
        Thread t2 = new Thread( r );
        t1.start();
        t2.start();
        try {
            //https://stackoverflow.com/questions/15956231/what-does-this-thread-join-code-mean
            //t1 and t2 threads have been running in parallel
            //but the main thread that started them needs to wait for them to finish before it can continue
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // each runs print different i
        System.out.println("if Synchronized:" + i);

    }

    public static void ifSynchronizedOnSameLock() {
        Object lock = new Object();
        Runnable r = () -> {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + " will sleep");
                try {
                    Thread.sleep(2000);//during the sleep, lock was hold, so other thread for this lock can't execute
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " wake up");
            }


        };
        Thread t1 = new Thread( r );
        Thread t2 = new Thread( r );
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public static void ifSynchronizedOnDifferentLock() {

        Runnable r = () -> {
            // lock on different object, then two thread will execute at same time
            Object lock = new Object();
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + " will sleep");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " wake up");
            }


        };
        Thread t1 = new Thread( r );
        Thread t2 = new Thread( r );
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

//    面试常考关于synchronized的7种情景
//    1.两个线程call同一个对象的同步方法?
//    只有一个线程先拿到锁进去,然后执行完,释放锁,第二个线程在拿锁再进去执行
//    2.两个线程call两个对象的同步方法。
//    因为锁是两个不同的实例,两个线程会同时执行/并行
//    3.两个线程call一个同步静态方法。
//    只有一个线程先拿到锁进去,然后执行完,释放锁,第二个线程在拿锁再进去执行
//    4.两个线程一个访问同步方法,另一个访问非同步方法。
//    两个线程会同时执行/并行,非同步方法不受影响
//    5。call一个对象的不同的同步方法,两个线程,一个call一个同步方法,另一个访问call一个同步方法。
//    只有一个线程先拿到锁进去,然后执行完,释放锁,第二个线程在拿锁再进去执行(锁是这个对象本身)
//    6。同时call静态的同步方法和非静态的同步方法。
//    一个锁是当前对象,一个是当前的Class对象,所以两个线程会同时执行
//    7。方法抛出异常后会不会释放锁?synchronized 会vs Lock 不会。
//    一个线程一旦抛出了异常,捕获了以后,会继续执行,执行完以后,然后释放锁,另外一个线程会开始执行

    public static void sameObjectDifferentSynchronizedMethod() {
        SynchronizedTest st = new SynchronizedTest();
        Runnable r1 = () -> {
            st.test1();
        };
        Runnable r2 = () -> {
            st.test2();
        };
        Thread t1 = new Thread( r1 );
        Thread t2 = new Thread( r2 );
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //https://www.imooc.com/video/18614
    public static void synchronizedWithException() {
        SynchronizedTest st = new SynchronizedTest();
        Runnable r3 = () -> {
            st.test3();
        };
        Runnable r2 = () -> {
            st.test2();
        };
        Thread t1 = new Thread( r3 );
        Thread t2 = new Thread( r2 );
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void test1() {
        System.out.println(Thread.currentThread().getName() + " will sleep:test1");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " wake up:test1");
    }

    public synchronized void test2() {
        System.out.println(Thread.currentThread().getName() + " will sleep:test2");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " wake up:test2");
    }

    public synchronized void test3() {
        System.out.println(Thread.currentThread().getName() + " will sleep:test3");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        try {
            throw new Exception();
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + " running:test3");

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

        System.out.println(Thread.currentThread().getName() + " done: test3");
    }

}


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。