zoukankan      html  css  js  c++  java
  • 2.2synchronized同步语句块

    使用synchronized虽然能够避免不同步的现象出现,但是也会出现弊端,比如代码执行时间过长,那么其他线程就必须等待该线程执行完毕释放锁之后才能拿到锁。

    面对这种问题可以使用同步代码块来解决。

    2.2.1synchronized方法的弊端:

    任务类:

    public class Task {
        private String getData1;
        private String getData2;
        synchronized public void doLongTimeTask() {
            try {
                System.out.println("begin task");
                Thread.sleep(3000);
                getData1 = "长时间处理任务后从远程返回的值1 threadName = "
                        + Thread.currentThread().getName();
                getData2 = "长时间处理任务后从远程返回的值2 threadName = "
                        + Thread.currentThread().getName();
                System.out.println(getData1);
                System.out.println(getData2);
                System.out.println("end task");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    工具类:

    public class CommonUtils {
        public static long beginTime1;
        public static long endTime1;
        public static long beginTime2;
        public static long endTime2;
    }

    线程代码1:

    public class Thread1 extends Thread {
        private Task task;
        public Thread1(Task task) {
            this.task = task;
        }
    
        @Override
        public void run() {
            CommonUtils.beginTime1 = System.currentTimeMillis();
            task.doLongTimeTask();
            CommonUtils.endTime1 = System.currentTimeMillis();
        }
    }

    线程代码2:

    public class Thread2 extends Thread {
        private Task task;
        public Thread2(Task task) {
            this.task = task;
        }
    
        @Override
        public void run() {
            CommonUtils.beginTime2 = System.currentTimeMillis();
            task.doLongTimeTask();
            CommonUtils.endTime2 = System.currentTimeMillis();
        }
    }

    执行代码:

    public class Main {
        public static void main(String[] args) {
            Task task = new Task();
            Thread1 thread1 = new Thread1(task);
            thread1.start();
            Thread2 thread2 = new Thread2(task);
            thread2.start();
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            long beginTime = CommonUtils.beginTime1;
            if (CommonUtils.beginTime2 < CommonUtils.beginTime1) {
                beginTime = CommonUtils.beginTime2;
            }
            long endTime = CommonUtils.endTime1;
            if (CommonUtils.endTime2 > CommonUtils.endTime1) {
                endTime = CommonUtils.endTime2;
            }
    
            System.out.println("耗时: " + (endTime - beginTime) / 1000);
    
        }
    }

    执行结果:

    从结果看这样运行一段代码耗时严重,解决这样的问题可以使用synchronized同步代码块。

    2.2.2synchronized同步代码块的使用:

    两个线程同时访问同一个对象的synchronized(this)同步代码块时,在代码运行期间只能有一个线程执行该段代码块,另一个线程必须等待当前线程完成执行才能够执行该段代码。

    模块业务类:

    public class ObjectService {
        public void serviceMethod() {
            try {
                synchronized (this) {
                    System.out.println("begin time = " + System.currentTimeMillis());
                    Thread.sleep(2000);
                    System.out.println("end time = " + System.currentTimeMillis());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    线程代码1:

    public class Thread3 extends Thread {
        private ObjectService objectService;
        public Thread3(ObjectService objectService) {
            this.objectService = objectService;
        }
    
        @Override
        public void run() {
            objectService.serviceMethod();
        }
    }

    线程代码2:

    public class Thread4 extends Thread {
        private ObjectService objectService;
        public Thread4(ObjectService objectService) {
            this.objectService = objectService;
        }
    
        @Override
        public void run() {
            objectService.serviceMethod();
        }
    }

    执行代码:

    public class Main {
        public static void main(String[] args) {
            ObjectService objectService = new ObjectService();
            Thread3 thread3 = new Thread3(objectService);
            thread3.setName("a");
            thread3.start();
            Thread4 thread4 = new Thread4(objectService);
            thread4.setName("b");
            thread4.start();
        }
    }

    执行结果:

    这样使用同步代码块,并没有使代码的效率提高,执行的效果还是同步执行的。下面的示例中解决synchronized同步代码块执行效率低的问题。

    2.2.3用同步代码块解决同步方法的弊端:

    任务类:

    public class DoLongTimeTask1 {
        private String getData1;
        private String getData2;
        public void doLongTimeTask() {
            try {
                System.out.println("begin task");
                Thread.sleep(3000);
                String privateData1 = "长时间处理任务后从后台远程返回的值1 threadName = "
                        + Thread.currentThread().getName();
                String privateData2 = "长时间处理任务后从后台远程返回的值2 threadName = "
                        + Thread.currentThread().getName();
                synchronized (this) {
                    getData1 = privateData1;
                    getData2 = privateData2;
                }
                System.out.println(getData1);
                System.out.println(getData2);
                System.out.println("end task");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    线程代码1:

    public class Thread1 extends Thread {
        private Task task;
        public Thread1(Task task) {
            this.task = task;
        }
    
        @Override
        public void run() {
            CommonUtils.beginTime1 = System.currentTimeMillis();
            task.doLongTimeTask();
            CommonUtils.endTime1 = System.currentTimeMillis();
        }
    }

    线程代码2:

    public class Thread2 extends Thread {
        private Task task;
        public Thread2(Task task) {
            this.task = task;
        }
    
        @Override
        public void run() {
            CommonUtils.beginTime2 = System.currentTimeMillis();
            task.doLongTimeTask();
            CommonUtils.endTime2 = System.currentTimeMillis();
        }
    }

    执行代码:

    public class Main {
        public static void main(String[] args) {
            Task task = new Task();
            Thread1 thread1 = new Thread1(task);
            thread1.start();
            Thread2 thread2 = new Thread2(task);
            thread2.start();
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            long beginTime = CommonUtils.beginTime1;
            if (CommonUtils.beginTime2 < CommonUtils.beginTime1) {
                beginTime = CommonUtils.beginTime2;
            }
            long endTime = CommonUtils.endTime1;
            if (CommonUtils.endTime2 > CommonUtils.endTime1) {
                endTime = CommonUtils.endTime2;
            }
    
            System.out.println("耗时: " + (endTime - beginTime) / 1000);
    
        }
    }

    执行结果:

    从上述可知当一个线程访问object中的synchronized同步代码块时,其他线程可以访问该object对象中非synchronized(this)同步代码块的内容。

    时间缩短,且运行效率加快,而且能够保持synchronized是同步的且当前线程持有锁。下面的示例进行验证。

    2.2.4一半异步,一半同步:

    事先说明:不在synchronized块中的代码使异步的,在synchronized中的代码是同步的。

    任务代码:

    public class Task1 {
        public void doLongTimeTask() {
            for (int i = 0; i < 100; i++) {
                System.out.println("nosynchronized  threadName = " + Thread.currentThread().getName() + "  i = " + (i + 1));
            }
            System.out.println("");
            synchronized (this) {
                for (int i = 0; i < 100; i++) {
                    System.out.println("synchronized  threadName = " + Thread.currentThread().getName() + "  i = " + (i + 1));
                }
            }
        }
    }

    线程代码1:

    public class Task1 {
        public void doLongTimeTask() {
            for (int i = 0; i < 100; i++) {
                System.out.println("nosynchronized  threadName = " + Thread.currentThread().getName() + "  i = " + (i + 1));
            }
            System.out.println("");
            synchronized (this) {
                for (int i = 0; i < 100; i++) {
                    System.out.println("synchronized  threadName = " + Thread.currentThread().getName() + "  i = " + (i + 1));
                }
            }
        }
    }

    线程代码2:

    public class Thread6 extends Thread {
        private Task1 task;
        public Thread6(Task1 task) {
            this.task = task;
        }
    
        @Override
        public void run() {
            task.doLongTimeTask();
        }
    }

    执行代码:

    public class Main {
        public static void main(String[] args) {
            Task1 task = new Task1();
            Thread5 thread5 = new Thread5(task);
            thread5.start();
            Thread6 thread6 = new Thread6(task);
            thread6.start();
    
        }
    }

    执行结果(左边为非同步,右边为同步):

    可以看出在同步代码块中的代码是同步运行的,而在非同步代码块中的代码是异步运行的。

    2.2.5synchronized代码块间的同步性:

    若一个线程访问了object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有的其他synchronized(this)同步代码块的访问将被阻塞。

    这个现象表明了:synchronized使用的是一个对象监视器。

    注:产生了疑惑,是否同步代码块与同步方法是同步的,然后做了测试发现同步代码块与同步方法之间是同步。(仅需要在同步代码块中调用同步方法,然后开启多条线程即可)(具体代码在fifthTask2与Thread7中)。

    也就是说说,对象监视器针对的是synchronized这个关键字。

    业务代码:

    public class ObjectService1 {
        public void serviceMethodA() {
            try {
                synchronized (this) {
                    System.out.println("A begin time = " +System.currentTimeMillis() );
                    System.out.println("A end time = " + System.currentTimeMillis());
                    Thread.sleep(3000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public void serviceMethodB() {
            synchronized (this) {
                System.out.println("B begin time = " +System.currentTimeMillis() );
                System.out.println("B end time = " + System.currentTimeMillis());
            }
        }
    }

    线程代码1:

    public class Thread8 extends Thread {
        private ObjectService1 objectService1;
    
        public Thread8(ObjectService1 objectService1) {
            this.objectService1 = objectService1;
        }
    
        @Override
        public void run() {
            objectService1.serviceMethodA();
        }
    }

    线程代码2:

    public class Thread9 extends Thread {
        private ObjectService1 objectService1;
    
        public Thread9(ObjectService1 objectService1) {
            this.objectService1 = objectService1;
        }
    
        @Override
        public void run() {
            objectService1.serviceMethodB();
        }
    }

    执行代码:

    public class Main {
        public static void main(String[] args) {
            ObjectService1 service = new ObjectService1();
            Thread8 a = new Thread8(service);
            a.setName("a");
            a.start();
            Thread9 b = new Thread9(service);
            b.setName("b");
            b.start();
        }
    }

    执行结果:

    可以看到不同的synchronized是存在同步关系的,即当一个线程获得锁后,对该对象的所有synchronized修饰的临界区都具有锁的效应。

    2.2.6验证同步synchronized(this)代码块时锁定当前对象的:

    synchronized修饰代码块时与synchronized修饰方法时是一样的都是锁定当前对象(对当前对象加锁)。

    任务代码:

    public class Task {
        public void method() {
            System.out.println("--------------run--method");
        }
    
        public void doLongTimeTask() {
            synchronized (this) {
                for (int i = 0; i < 1000; i++) {
                    System.out.println("synchronized threadName = " +
                            Thread.currentThread().getName() + " i = " + (i + 1));
                }
            }
        }
    }

    线程代码1:

    public class Thread10 extends Thread {
        private Task task;
        public Thread10(Task task) {
            this.task = task;
        }
    
        @Override
        public void run() {
            task.doLongTimeTask();
        }
    }

    线程代码2:

    public class Thread11 extends Thread {
        private Task task;
        public Thread11(Task task) {
            this.task = task;
        }
    
        @Override
        public void run() {
            task.method();
        }
    }

    执行代码:

    public class Main {
        public static void main(String[] args) throws Exception{
            Task task = new Task();
            Thread10 thread10 = new Thread10(task);
            thread10.start();
            Thread.sleep(10);
            Thread11 thread11 = new Thread11(task);
            thread11.start();
        }
    }

    执行结果:

    修改任务代码:

    public class Task {
        synchronized public void method() {
            System.out.println("--------------run--method");
        }
    
        public void doLongTimeTask() {
            synchronized (this) {
                for (int i = 0; i < 100000; i++) {
                    System.out.println("synchronized threadName = " +
                            Thread.currentThread().getName() + " i = " + (i + 1));
                }
            }
        }
    }

    执行结果:

    2.2.7将任意对象作为对象监视器:

    多个线程调用同一个对象的synchronized同步方法或者synchronized(this)同步代码块时,调用的方法时按顺序执行的,是同步的是阻塞的。

    说明synchronized同步方法与synchronized(this)同步代码块有两种作用。

    synchronized同步方法

    • 对其他synchronized同步方法或synchronized(this)同步代码块调用起阻塞作用。
    • 同一时间只有一个线程可以执行synchronized修饰的隔离区中的代码

    synchronized(this)同步代码块

    • 对其他synchronized同步方法或synchronized(this)同步代码块调用起阻塞作用。
    • 同一时间只有一个线程可以执行synchronized修饰的隔离区中的代码

    在前面的学习中,使用synchronized(this)格式来同步代码块,其实java还支持对“任意对象”作为“对象监视器”来实现同步功能,这个“任意对象”大多数是实例变量及方法的参数,使用格式为synchronized(非this对象)。

    synchronized(非this对象)格式的作用只有1种,synchronized(非this对象X)同步代码块。

    在多个线程持有“对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象X)同步代码块中的代码。

    当持有对象监视器为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象X)同步代码块中的代码。

    用户业务代码:

    public class UserService {
        private String usernameParam;
        private String passwordParam;
        private String anyString = new String();
        public void setUsernamePassword(String username, String password) {
            try {
                synchronized (anyString) {
                    System.out.println("线程名称为: " + Thread.currentThread().getName() +
                    " 在 "+ System.currentTimeMillis() + " 进入同步代码块");
                    usernameParam = username;
                    Thread.sleep(1000);
                    passwordParam = password;
                    System.out.println("线程名称为: " + Thread.currentThread().getName() +
                    " 在 " +System.currentTimeMillis() + " 离开同步代码块");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    线程代码1:

    public class Thread12 extends Thread {
        private UserService service;
        public Thread12(UserService service) {
            this.service = service;
        }
    
        @Override
        public void run() {
            service.setUsernamePassword("a","aa");
        }
    }

    线程代码2:

    public class Thread13 extends Thread {
        private UserService service;
        public Thread13(UserService service) {
            this.service = service;
        }
    
        @Override
        public void run() {
            service.setUsernamePassword("b","bb");
        }
    }

    执行代码:

    public class Main {
        public static void main(String[] args) throws Exception{
            UserService service = new UserService();
            Thread12 a = new Thread12(service);
            a.setName("A");
            a.start();
            Thread13 b = new Thread13(service);
            b.setName("B");
            b.start();
        }
    }

    执行结果:

    锁定非this对象具有的优点:如果在一个类中有很多个synchronized方法,这时虽然能实现同步,但会受阻,所以影响效率;但如果使用同步代码块锁非this对象,则synchronized(非this)daima

     

     

  • 相关阅读:
    leetcode56 合并区间(Medium)
    leetcode215 数组中的第k大元素(Medium)
    leetcode76 最小覆盖子串(Hard)
    leetcode75 颜色分类(Medium)
    leetcode64 最小路径和(Medium)
    Android 开发技巧
    Android 常用开源代码整理
    Android 屏幕尺寸知识
    Intellij IDEA 常用 设置 及 快捷键 (持续更新)
    Gradle Maven 依赖管理
  • 原文地址:https://www.cnblogs.com/lilinzhiyu/p/7994275.html
Copyright © 2011-2022 走看看