(三) 线程间的共享和协作 之 synchronized关键字

时间:2020-05-21
本文章向大家介绍(三) 线程间的共享和协作 之 synchronized关键字,主要包括(三) 线程间的共享和协作 之 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            
//            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会穿插执行 也就是不能保证在不同对象下,线程安全

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才会执行 也就是可以保证在同一对象下,线程安全

测试结论

对象 同步代码块 同步普通方法 同步静态方法
单个实例 线程安全 线程安全 线程安全
多个实例 线程之间对方法互不干扰,但是对同一变量不安全 线程之间对方法互不干扰,但是对同一变量不安全 线程安全

$flag 上一页 下一页