zoukankan      html  css  js  c++  java
  • (三) 线程间的共享和协作 之 synchronized关键字

    synchronized 关键字存在的意义

    线程开始运行,拥有自己的栈空间,就如同一个脚本一样,按照既定的代码一步一步地执行,直到终止。但是,每个运行中的线程,如果仅仅是孤立地运行,那么没有一点儿价值,或者说价值很少,如果多个线程能够相互配合完成工作,包括数据之间的共享,协同处理事情。这将会带来巨大的价值。

    Java支持多个线程同时访问一个对象或者对象的成员变量,关键字synchronized可以修饰方法或者以同步块的形式来进行使用,它的目的是确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性,又称为内置锁机制。

    对象锁和类锁

    java的对象锁和类锁在锁的概念上基本上和内置锁是一致的,但是,两个锁实际是有很大的区别的,对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。
    我们知道,类的对象实例可以有很多个,但是每个类只有一个class对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的

    这个特性也是解决在分布式场景下,synchronized的适用方法

    synchronized 关键字的三种使用方式

    1. 对于同步代码块,就得指定锁对象。
    2. 对于修饰方法的synchronized,默认的锁对象就是当前方法的对象。
    3. 对于修饰静态方法的synchronized,其锁对象就是此方法所对应的类Class对象。(分布式)

    实战解析

    No1.同步代码块

        /**
         * synchronized 关键字测试
         * <p>
         * 同步代码块
         * 结果是 所有打印语句中 startDate 是同时进入的 在多线程情况下  只允许一个线程进入 同步代码块
         * 当new 出两个 对象的时候  同步代码块 将互不干扰
         * 结论 针对同一对象 同步代码块可以起到多线程访问不会冲突的情况
         */
        private static class SynchronizedDemo1 implements Runnable {
            /**
             * 全局变量
             * 创建一个计数器
             */
            private static int counter = 1;
    
            public void run() {
                long startDate = System.currentTimeMillis();
                synchronized (this) {
                    for (int i = 0; i < 5; i++) {
                        try {
                            System.out.println("线程 :" + Thread.currentThread().getName() + " 当前计数器 :" + (counter++));
                            System.out.println("开始时间 :" + startDate + " 当前时间 :" + System.currentTimeMillis());
                            System.out.println();
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
    
            public static void main(String[] args) {
    // 测试代码1
    //            SynchronizedDemo1 syncThread = new SynchronizedDemo1();
    //            Thread thread1 = new Thread(syncThread, "sync-thread-1");
    //            Thread thread2 = new Thread(syncThread, "sync-thread-2");
    //            thread1.start();
    //            thread2.start();
    // 测试代码2
    //            SynchronizedDemo1 syncThread1 = new SynchronizedDemo1();
    //            SynchronizedDemo1 syncThread2 = new SynchronizedDemo1();
    //            Thread thread1 = new Thread(syncThread1, "sync-thread-1");
    //            Thread thread2 = new Thread(syncThread2, "sync-thread-2");
    //            thread1.start();
    //            thread2.start();
            }
        }
    

    测试结果
    测试代码1


    发现线程1执行完之后线程2才会执行 也就是可以保证在同一对象下,线程安全

    测试代码2

    发现线程1执行期间线程2会穿插执行 也就是不能保证在不同对象下,线程安全

    No2.普通方法测试

        /**
         * synchronized 普通方法测试
         * <p>
         * 在同一对象中 即使多个线程访问同一方法 只允许一个线程进入 加锁方法
         * <p>
         * 当new 出两个 对象的时候  同步代码块 将互不干扰
         */
        private static class SynchronizedDemo2 implements Runnable {
            /**
             * 全局变量
             * 创建一个计数器
             */
            private static int counter = 1;
    
            public synchronized void run() {
                long startDate = System.currentTimeMillis();
                for (int i = 0; i < 5; i++) {
                    try {
                        System.out.println("线程 :" + Thread.currentThread().getName() + " 当前计数器 :" + (counter++));
                        System.out.println("开始时间 :" + startDate + " 当前时间 :" + System.currentTimeMillis());
                        System.out.println();
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
    
    
            public static void main(String[] args) {
    // 测试代码1            
    //            SynchronizedDemo2 syncThread = new SynchronizedDemo2();
    //            Thread thread1 = new Thread(syncThread, "sync-thread-1");
    //            Thread thread2 = new Thread(syncThread, "sync-thread-2");
    //            thread1.start();
    //            thread2.start();
    
    // 测试代码2            
    //            SynchronizedDemo2 syncThread1 = new SynchronizedDemo2();
    //            SynchronizedDemo2 syncThread2 = new SynchronizedDemo2();
    //            Thread thread1 = new Thread(syncThread1, "sync-thread-1");
    //            Thread thread2 = new Thread(syncThread2, "sync-thread-2");
    //            thread1.start();
    //            thread2.start();
            }
        }
    

    测试结果
    测试代码1


    发现线程1执行完之后线程2才会执行 也就是可以保证在同一对象下,线程安全

    测试代码2

    发现线程1执行期间线程2会穿插执行 也就是不能保证在不同对象下,线程安全

    No3.静态方法测试

        /**
         * synchronized 静态方法测试
         * 会将这个类锁  也就是对于的class 锁起来 这个类的实例都会被锁
         */
       public static class SynchronizedDemo3 implements Runnable {
    
            private static int counter = 1;
    
            /**
             * 静态的同步方法
             */
            public synchronized static void method() {
                long startDate = System.currentTimeMillis();
                for (int i = 0; i < 5; i++) {
                    try {
                        System.out.println("线程 :" + Thread.currentThread().getName() + " 当前计数器 :" + (counter++));
                        System.out.println("开始时间 :" + startDate + " 当前时间 :" + System.currentTimeMillis());
                        System.out.println();
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            public void run() {
                method();
            }
            
            public static void main(String[] args) {
    // 测试方法1            
    //            SynchronizedDemo3 syncThread1 = new SynchronizedDemo3();
    //            Thread thread1 = new Thread(syncThread1, "sync-thread-1");
    //            Thread thread2 = new Thread(syncThread1, "sync-thread-2");
    //            thread1.start();
    //            thread2.start();
                
    // 测试方法2
    //            SynchronizedDemo3 syncThread1 = new SynchronizedDemo3();
    //            SynchronizedDemo3 syncThread2 = new SynchronizedDemo3();
    //            Thread thread1 = new Thread(syncThread1, "sync-thread-1");
    //            Thread thread2 = new Thread(syncThread1, "sync-thread-2");
    //            thread1.start();
    //            thread2.start();
            }
        }
    

    测试结果
    测试代码1


    发现线程1执行完之后线程2才会执行 也就是可以保证在同一对象下,线程安全

    测试代码2

    发现线程1执行完之后线程2才会执行 也就是可以保证在同一对象下,线程安全

    测试结论

    对象 同步代码块 同步普通方法 同步静态方法
    单个实例 线程安全 线程安全 线程安全
    多个实例 线程之间对方法互不干扰,但是对同一变量不安全 线程之间对方法互不干扰,但是对同一变量不安全 线程安全
  • 相关阅读:
    Silverlight 数据绑定 (1):怎样实现数据绑定
    DynamicPopulateExtender 控件调 WebService 的500错误
    [翻译]Linq 的 7 个技巧简化程序操作
    [Silverlight] 一个易犯的错误:关于调用 WCF 服务
    Silverlight 数据绑定 (2):Source to Target
    KB kb KB大小写
    C# winform 程序中响应键盘事件
    异常“企图释放并非呼叫方所拥有的多用户终端运行程序”的处理
    php完美截取中文字符函数mb_substr
    php面试题(三)
  • 原文地址:https://www.cnblogs.com/monco-sxy/p/12931525.html
Copyright © 2011-2022 走看看