美文网首页
多线程中sychronized修饰符

多线程中sychronized修饰符

作者: 叫我胖虎大人 | 来源:发表于2019-08-13 22:35 被阅读0次

    线程安全问题的主要诱因

    • 存在共享数据(也称临界资源)
    • 存在多条线程共同操作这些共享数据

    解决问题的根本方法:
    同一时刻有且只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再对共享数据进行操作


    互斥锁的特性

    • 互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这个特性来实现多线程的协调机制 ,这样在同一时间只有一个线程对需要同步的代码块(复合操作)进行访问.互斥性又称为操作的原子性.

    • *可见性:8必须确保在锁被释放之前,对共享变量所做的修稿,对于随后获得该所的拧一个线程是可见的(即在获得锁时应当获得最新共享变量的值),否则另一个线程可能是在本地缓存的某一个副本上继续操作,从而额引起不一致.


    根据获取的锁的分类

    • 获取对象锁
      • 1.同步代码块(synchronized(this),sychronized(类实例对象)),锁是小括号中的实例对象
      • 2.同步非静态方法(synchronized method),锁是当前对象的实例对象
    • 获取类锁
      • 1.同步代码块(synchronized(类.class)),锁是小括号()中类对象(Class对象)
      • 2.同步静态方法(synchronized static method),锁是当前对象的类对象(Class对象)

    代码示例

    SyncThread(线程类)

    
    /**
     * 不同的线程name属性,执行不同的run()方法
     * @author panghu
     */
    public class SyncThread implements Runnable {
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            //根据前缀判断执行的run()方法
            if (threadName.startsWith("A")) {
                async();
            } else if (threadName.startsWith("B")) {
                syncObjectBlock1();
            } else if (threadName.startsWith("C")) {
                syncObjectMethod1();
            } else if (threadName.startsWith("D")) {
                syncClassBlock1();
            } else if (threadName.startsWith("E")) {
                syncClassMethod1();
            }
        }
    
        /**
         * 异步方法,不需要获取锁
         */
        private void async(){
            try{
                System.out.println(Thread.currentThread().getName() + "_Async_Start: "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + "_Async_End: "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    
        /**
         * 方法中有 synchronized(this|object) {} 同步代码块
         *
         * 先进入方法,再获取对象锁
         */
        private void syncObjectBlock1(){
            System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1: "
                    + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            synchronized (this){
                try{
                    System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1_Start: "
                            + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1_End: "
                            + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
    
    
        /**
         * synchronized 修饰非静态方法
         * 进入方法之前就需要获取对象锁
         */
        private synchronized void syncObjectMethod1() {
            System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1: "
                    + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            try {
                System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1_Start: "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1_End: "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 同步代码块(synchronized(类.class))
         */
        private void syncClassBlock1() {
            System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1: "
                    + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            synchronized (SyncThread.class) {
                try {
                    System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1_Start: "
                            + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1_End: "
                            + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        /**
         * 同步静态方法(synchronized static method)
         */
        private synchronized static void syncClassMethod1() {
            System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1: "
                    + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            try {
                System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1_Start: "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1_End: "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    SyncDemo(测试类)

    测试样例一

    public class SyncDemo {
        public static void main(String... args) {
            SyncThread syncThread = new SyncThread();
            Thread A_thread1 = new Thread(syncThread, "A_thread1");
            Thread A_thread2 = new Thread(syncThread, "A_thread2");
            //B1,B2,C1,C2共用同一个对象
            Thread B_thread1 = new Thread(syncThread, "B_thread1");
            Thread B_thread2 = new Thread(syncThread, "B_thread2");
            Thread C_thread1 = new Thread(syncThread, "C_thread1");
            Thread C_thread2 = new Thread(syncThread, "C_thread2");
    //        Thread D_thread1 = new Thread(syncThread, "D_thread1");
    //        Thread D_thread2 = new Thread(syncThread, "D_thread2");
    //        Thread E_thread1 = new Thread(syncThread, "E_thread1");
    //        Thread E_thread2 = new Thread(syncThread, "E_thread2");
            A_thread1.start();
            A_thread2.start();
            B_thread1.start();
            B_thread2.start();
            C_thread1.start();
            C_thread2.start();
    //        D_thread1.start();
    //        D_thread2.start();
    //        E_thread1.start();
    //        E_thread2.start();
        }
    }
    
    

    运行结果

    A_thread1_Async_Start: 22:07:38
    A_thread2_Async_Start: 22:07:38
    C_thread1_SyncObjectMethod1: 22:07:38
    B_thread1_SyncObjectBlock1: 22:07:38
    B_thread2_SyncObjectBlock1: 22:07:38
    C_thread1_SyncObjectMethod1_Start: 22:07:38
    A_thread1_Async_End: 22:07:39
    A_thread2_Async_End: 22:07:39
    C_thread1_SyncObjectMethod1_End: 22:07:39
    B_thread2_SyncObjectBlock1_Start: 22:07:39
    B_thread2_SyncObjectBlock1_End: 22:07:40
    B_thread1_SyncObjectBlock1_Start: 22:07:40
    B_thread1_SyncObjectBlock1_End: 22:07:41
    C_thread2_SyncObjectMethod1: 22:07:41
    C_thread2_SyncObjectMethod1_Start: 22:07:41
    C_thread2_SyncObjectMethod1_End: 22:07:42

    结论:
    线程A1,A2单独运行,不影响其他其他线程的运行,同样的其他线程(B1,B2,C1,C2)同样也不影响它们的单独运行.
    当线程(B1,B2,C1,C2)其中一个(假设是C1)线程持有同一对象的对象锁之后,B1,B2,C2需要等C1释放对象锁之后才能进入sychronized修饰的模块


    测试样例二
    将持有的syncThread对象,改为new SyncThread(),这样每个线程拥有的对象就是不同的

    public class SyncDemo {
        public static void main(String... args) {
            SyncThread syncThread = new SyncThread();
            Thread A_thread1 = new Thread(syncThread, "A_thread1");
            Thread A_thread2 = new Thread(syncThread, "A_thread2");
            //B1,B2,C1,C2共用同一个对象
            Thread B_thread1 = new Thread(new SyncThread(), "B_thread1");
            Thread B_thread2 = new Thread(new SyncThread(), "B_thread2");
            Thread C_thread1 = new Thread(new SyncThread(), "C_thread1");
            Thread C_thread2 = new Thread(new SyncThread(), "C_thread2");
    //        Thread D_thread1 = new Thread(syncThread, "D_thread1");
    //        Thread D_thread2 = new Thread(syncThread, "D_thread2");
    //        Thread E_thread1 = new Thread(syncThread, "E_thread1");
    //        Thread E_thread2 = new Thread(syncThread, "E_thread2");
            A_thread1.start();
            A_thread2.start();
            B_thread1.start();
            B_thread2.start();
            C_thread1.start();
            C_thread2.start();
    //        D_thread1.start();
    //        D_thread2.start();
    //        E_thread1.start();
    //        E_thread2.start();
        }
    }
    

    运行结果:

    A_thread2_Async_Start: 22:16:37
    C_thread2_SyncObjectMethod1: 22:16:37
    C_thread1_SyncObjectMethod1: 22:16:37
    C_thread2_SyncObjectMethod1_Start: 22:16:37
    A_thread1_Async_Start: 22:16:37
    B_thread1_SyncObjectBlock1: 22:16:37
    B_thread2_SyncObjectBlock1: 22:16:37
    C_thread1_SyncObjectMethod1_Start: 22:16:37
    B_thread1_SyncObjectBlock1_Start: 22:16:37
    B_thread2_SyncObjectBlock1_Start: 22:16:37
    A_thread2_Async_End: 22:16:38
    A_thread1_Async_End: 22:16:38
    C_thread2_SyncObjectMethod1_End: 22:16:38
    C_thread1_SyncObjectMethod1_End: 22:16:38
    B_thread2_SyncObjectBlock1_End: 22:16:38
    B_thread1_SyncObjectBlock1_End: 22:16:38

    结论:线程B1,B2,C1,C2所持有的对象锁不同,互不影响


    测试样例三

    public class SyncDemo {
        public static void main(String... args) {
            SyncThread syncThread = new SyncThread();
            Thread A_thread1 = new Thread(syncThread, "A_thread1");
            Thread A_thread2 = new Thread(syncThread, "A_thread2");
            //B1,B2,C1,C2共用同一个对象
    //        Thread B_thread1 = new Thread(new SyncThread(), "B_thread1");
    //        Thread B_thread2 = new Thread(new SyncThread(), "B_thread2");
    //        Thread C_thread1 = new Thread(new SyncThread(), "C_thread1");
    //        Thread C_thread2 = new Thread(new SyncThread(), "C_thread2");
            Thread D_thread1 = new Thread(syncThread, "D_thread1");
            Thread D_thread2 = new Thread(syncThread, "D_thread2");
            Thread E_thread1 = new Thread(syncThread, "E_thread1");
            Thread E_thread2 = new Thread(syncThread, "E_thread2");
            A_thread1.start();
            A_thread2.start();
    //        B_thread1.start();
    //        B_thread2.start();
    //        C_thread1.start();
    //        C_thread2.start();
            D_thread1.start();
            D_thread2.start();
            E_thread1.start();
            E_thread2.start();
        }
    }
    

    运行结果:

    A_thread1_Async_Start: 22:19:03
    D_thread2_SyncClassBlock1: 22:19:03
    D_thread1_SyncClassBlock1: 22:19:03
    E_thread1_SyncClassMethod1: 22:19:03
    A_thread2_Async_Start: 22:19:03
    E_thread1_SyncClassMethod1_Start: 22:19:03
    A_thread1_Async_End: 22:19:04
    A_thread2_Async_End: 22:19:04
    E_thread1_SyncClassMethod1_End: 22:19:04
    D_thread1_SyncClassBlock1_Start: 22:19:04
    D_thread1_SyncClassBlock1_End: 22:19:05
    D_thread2_SyncClassBlock1_Start: 22:19:05
    D_thread2_SyncClassBlock1_End: 22:19:06
    E_thread2_SyncClassMethod1: 22:19:06
    E_thread2_SyncClassMethod1_Start: 22:19:06
    E_thread2_SyncClassMethod1_End: 22:19:07

    结论:当线程(D1,D2,E1,E2)其中一个(假设是E1)线程持有同一对象的对象锁之后,D1,D2,E2需要等E1释放类锁之后才能进入sychronized修饰的模块.


    测试样例四

    public class SyncDemo {
        public static void main(String... args) {
            SyncThread syncThread = new SyncThread();
            Thread A_thread1 = new Thread(syncThread, "A_thread1");
            Thread A_thread2 = new Thread(syncThread, "A_thread2");
            //B1,B2,C1,C2共用同一个对象
    //        Thread B_thread1 = new Thread(new SyncThread(), "B_thread1");
    //        Thread B_thread2 = new Thread(new SyncThread(), "B_thread2");
    //        Thread C_thread1 = new Thread(new SyncThread(), "C_thread1");
    //        Thread C_thread2 = new Thread(new SyncThread(), "C_thread2");
            Thread D_thread1 = new Thread(new SyncThread(), "D_thread1");
            Thread D_thread2 = new Thread(new SyncThread(), "D_thread2");
            Thread E_thread1 = new Thread(new SyncThread(), "E_thread1");
            Thread E_thread2 = new Thread(new SyncThread(), "E_thread2");
            A_thread1.start();
            A_thread2.start();
    //        B_thread1.start();
    //        B_thread2.start();
    //        C_thread1.start();
    //        C_thread2.start();
            D_thread1.start();
            D_thread2.start();
            E_thread1.start();
            E_thread2.start();
        }
    }
    
    

    运行结果

    A_thread2_Async_Start: 22:23:59
    A_thread1_Async_Start: 22:23:59
    D_thread1_SyncClassBlock1: 22:23:59
    E_thread1_SyncClassMethod1: 22:23:59
    D_thread2_SyncClassBlock1: 22:23:59
    E_thread1_SyncClassMethod1_Start: 22:23:59
    A_thread2_Async_End: 22:24:00
    A_thread1_Async_End: 22:24:00
    E_thread1_SyncClassMethod1_End: 22:24:00
    D_thread2_SyncClassBlock1_Start: 22:24:00
    D_thread2_SyncClassBlock1_End: 22:24:01
    D_thread1_SyncClassBlock1_Start: 22:24:01
    D_thread1_SyncClassBlock1_End: 22:24:02
    E_thread2_SyncClassMethod1: 22:24:02
    E_thread2_SyncClassMethod1_Start: 22:24:02
    E_thread2_SyncClassMethod1_End: 22:24:03

    结论:同上述示例3 的结论,将syncThread修改为new SyncThread()获取的依旧是同一个类对象,所以结论相同.

    对象锁和类锁的总结

    • 1.有线程访问对象的同步代码块时,另外的线程可以访问该对象的非同步代码块;
    • 2.若锁住的是同一个对象,一个线程在访问对象的同步代码块时,另一个访问对象的同步代码块的线程会被阻塞;
    • 3.若锁住的是同一个对象,一个线程在访问对象的同步方法时,另一个访问对象的同步方法线程会被阻塞;
    • 4.若锁住的是同一个对象,一个线程在访问对象的同步代码块时,另一个访问对象的同步方法的线程会被阻塞,反之亦然;
    • 5.同一个类的不同对象的对象锁互不干扰;
    • 6.类锁由于也是一种特殊的对象锁,因此和上述的1,2,3,4一致,而由于一个类只有一把对象锁,所以一个类使用类锁将会是同步的;
    • 7.类锁和对象锁互补干扰.(忘了演示,😳)

    待补充

    相关文章

      网友评论

          本文标题:多线程中sychronized修饰符

          本文链接:https://www.haomeiwen.com/subject/ndsijctx.html