图片 9

2.2synchronized同步语句块

嗯,好像是这么回事,一旦失去焦点了,程序就暂停了,后面的代码肯定不执行了。那一会儿回来后,还执行
onblur 中的代码吗?不会。

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

Java将被 { }
包裹的部分称之为代码块。但是这部分知识在开发中很少被使用到,不过大家在面试过程中可能会被这个小东西虐到。

这个在这篇文章中也有验证,一旦离开之后,定时器都不执行了:手机上与电脑上对
setInterval 的不同处理

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

  • 分类

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);

    }
}

执行结果:

图片 1

从结果看这样运行一段代码耗时严重,解决这样的问题可以使用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();
    }
}

执行结果:

图片 2

这样使用同步代码块,并没有使代码的效率提高,执行的效果还是同步执行的。下面的示例中解决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);

    }
}

执行结果:

图片 3

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

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

  1. 局部代码块
    写在方法中的变量我们叫做局部变量,同样,写在方法中的代码块我们叫做局部代码块,它的用途不广,我了解仅限其用于限制变量的生命周期。

    public void method() {
        {
             int x = 10;
             System.out.println("x的值为" + x);
        }
             System.out.println("x的值为" + x);
    } 
    

    以上的方法会在执行输出一个x的值为10之后向我们抛出一个错误找不到符号x变量x只能在局部块中使用,局部块执行完毕,x就被销毁,就如同在for循环中定义的循环变量一般。

  2. 构造代码块
    写在类中方法外的代码块被称之为构造代码块,它在会在执行该类构造方法的时候被执行。也就是说,多次执行构造方法就会多次执行构造代码块。

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();

    }
}

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

图片 4图片 5

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

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();
    }
}

执行结果:

图片 6

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

public class Student {
    private int age;
    {
         System.out.println("我是构造代码块!!");
    }
    public Student() {
         System.out.println("我是无参构造!!");
    }
    public Student(int age) {
         this.age = age;
         System.out.println("我是有参构造!!");
    }
} 

public class MainClass {
    public static void main(String[] args) {
         Student s1 = new Student();
         Student s2 = new Student(15);
    }
}

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();
    }
}

执行结果:

图片 7

修改任务代码:

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));
            }
        }
    }
}

执行结果:

图片 8

输出结果:

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();
    }
}

执行结果:

图片 9

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

 

我是构造代码块!!
我是无参构造!!
我是构造代码块!!
我是有参构造!!

 

 

 

 

 

 

 

 

我们能够很清楚的看见构造代码块是优先于构造方法执行的。

 

 

  1. 静态代码块
    被加了static修饰的代码块我们称之为静态代码块,静态代码块回在类被加载入方法区的时候被执行一次。往后不再执行。

public class Student {
    private int age;
    static {
         System.out.println("我是静态代码块!!");
    }
    public Student() {
         System.out.println("我是无参构造!!");
    }
    public Student(int age) {
         this.age = age;
         System.out.println("我是有参构造!!");
    }
} 
public class MainClass {
    public static void main(String[] args) {
         Student s1 = new Student();
         Student s2 = new Student(15);
    }
}

输出结果:

我是静态代码块!!
我是无参构造!!
我是有参构造!!

静态代码块仅仅执行了一遍。

注意:
静态代码块和构造代码块都是先于构造方法执行,那么他们之间又是谁先谁后呢?其实我已经再文中做了介绍,不知道大家有没有注意到
构造代码块是在执行构造方法时被先一步执行
静态代码块是在类加载时就被执行
自然两者之间是静态代码块先被执行。

  1. 同步代码块
    待以后更新到多线程时再做详解。
  • 那么问题来了!!

class Student {
    static {
        System.out.println("Student 静态代码块");
    }

    {
        System.out.println("Student 构造代码块");
    }

    public Student() {
        System.out.println("Student 构造方法");
    }
}

class MainClass {
    static {
        System.out.println("MainClass静态代码块");
    }

    public static void main(String[] args) {
        System.out.println("我是main方法");

        Student s1 = new Student();
        Student s2 = new Student();
    }
}

它的输出结果是什么?

发表评论

电子邮件地址不会被公开。 必填项已用*标注