• Java多线程--synchronized(三)


    synchronized(object) 非this对象锁

    package ch02.test4;
    
    
    import java.util.ArrayList;
    import java.util.List;
    
    class MyList {
        private List list = new ArrayList();
        public synchronized void add(String data) {
            list.add(data);
        }
        public synchronized int getSize() {
            return list.size();
        }
    }
    
    class MyService {
        public MyList addServiceMethod(MyList list, String data) {
            try {
                if(list.getSize() < 1) {
                    Thread.sleep(2000);
                    list.add(data);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return list;
        }
    }
    
    class MyThread1 extends Thread {
        private MyList list;
    
        public MyThread1(MyList list) {
            this.list = list;
        }
    
        @Override
        public void run() {
            super.run();
            MyService myService = new MyService();
            myService.addServiceMethod(list,"A");
        }
    }
    
    class MyThread2 extends Thread {
        private MyList list;
    
        public MyThread2(MyList list) {
            this.list = list;
        }
    
        @Override
        public void run() {
            super.run();
            MyService myService = new MyService();
            myService.addServiceMethod(list,"B");
        }
    }
    
    public class Test {
        public static void main(String[] args) throws InterruptedException {
            MyList list = new MyList();
            MyThread1 myThread1 = new MyThread1(list);
            myThread1.setName("A");
            myThread1.start();
            MyThread2 myThread2 = new MyThread2(list);
            myThread2.setName("B");
            myThread2.start();
            Thread.sleep(6000);
            System.out.println("listSize="+list.getSize());
        }
    }
    

    output:
    listSize=2
    出现“脏读”的原因是两个线程以异步的方式返回list的大小。
    解决方法:
    修改MyService类:

    class MyService {
        public MyList addServiceMethod(MyList list, String data) {
            try {
                synchronized (list) {
                    if (list.getSize() < 1) {
                        Thread.sleep(2000);
                        list.add(data);
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return list;
        }
    }
    

    修改之后,对同一个list对象,在代码块中,线程A始终保持对list对象的锁,因而线程B在线程A完成add操作之前不能执行list的同步方法。

    synchronized静态同步方法和synchronized(class)代码块

    1. synchronized静态同步方法
      synchronized加到static方法是给Class类上锁,而加到非static方法是给对象上锁。
    package ch02.test5;
    
    
    
    public class Test {
        Service service = new Service();
    
        public static void main(String[] args) {
            Service service = new Service();
            ThreadA threadA = new ThreadA(service);
            threadA.setName("a");
            threadA.start();
            ThreadB threadB = new ThreadB(service);
            threadB.setName("b");
            threadB.start();
            ThreadC threadC = new ThreadC(service);
            threadC.setName("c");
            threadC.start();
        }
    }
    
    class Service {
        synchronized public static void printA() {
            try {
                System.out.println("线程"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"进入printA");
                Thread.sleep(3000);
                System.out.println("线程"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"离开printA");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        synchronized public static void printB() {
                System.out.println("线程"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"进入printB");
                System.out.println("线程"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"离开printB");
        }
        synchronized public void printC() {
                System.out.println("线程"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"进入printC");
                System.out.println("线程"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"离开printC");
        }
    }
    
    class ThreadA extends Thread {
        private Service service;
    
        public ThreadA(Service service) {
            this.service = service;
        }
    
        @Override
        public void run() {
            super.run();
            service.printA();
        }
    }
    
    class ThreadB extends Thread {
        private Service service;
    
        public ThreadB(Service service) {
            this.service = service;
        }
    
        @Override
        public void run() {
            super.run();
            service.printB();
        }
    }
    class ThreadC extends Thread {
        private Service service;
    
        public ThreadC(Service service) {
            this.service = service;
        }
    
        @Override
        public void run() {
            super.run();
            service.printC();
        }
    }
    

    output:

    线程a在1599825585555进入printA
    线程c在1599825585564进入printC
    线程c在1599825585564离开printC
    线程a在1599825588556离开printA
    线程b在1599825588556进入printB
    线程b在1599825588556离开printB
    

    从结果上来看,线程A和线程B之间是同步的,AC之间以及BC之间是异步的。

    原因是线程A和线程B分别持有不同的锁,线程A持有的是Class锁,线程B持有的是对象锁。

    作者:lykxbg —— 来一块小饼干

    出处:http://www.cnblogs.com/lykxbg/

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    模拟死锁
    B站学习斯坦福大学Swift 语言教程 iOS11 开发【第一集】踩到的几个坑(XCode 13.2.1版本)
    数学之美番外篇:平凡而又神奇的贝叶斯方法
    joj 1753: Street Numbers
    二叉树的三种遍历(递归+非递归)
    joj 1905: Freckles
    joj 2630: A Pair of Graphs(同构图的判定)
    vue3.x 中获取dom元素
    defineProperty 和 Proxy 的区别
    vue 按钮的防抖和节流
  • 原文地址:https://www.cnblogs.com/lykxbg/p/13654435.html
Copyright © 2020-2023  润新知