三. synchronized同步语句块

1. synchronized方法的弊端

用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个长时间的任务,那么B线程则必须等待比较长时间,在这种情况下可以使用synchronized同步语句块来解决。

这句话好理解,demo 略,

2. 用同步代码块解决同步方法的弊端

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

public class Task {
    private String getData1;
    private String getData2;
    public void doLongTimeTask() {
        try {
            System.out.println("begin task");
            Thread.sleep(3000);

            String privateGetData1 = "长时间处理任务后的返回的值 1 threadName="
                    + Thread.currentThread().getName();
            String privateGetData2 = "长时间处理任务后的返回的值 2 threadName="
                    + Thread.currentThread().getName();
            synchronized (this) {
                getData1 = privateGetData1;
                getData2 = privateGetData2;
            }
            System.out.println(getData1);
            System.out.println(getData2);
            System.out.println("end task");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

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

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

public class Run {

    public static void main(String[] args) {
        Task task = new Task();

        MyThread1 thread1 = new MyThread1(task);
        thread1.start();

        MyThread2 thread2 = new MyThread2(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));
    }
}
运行效果:
begin task
begin task
长时间处理任务后的返回的值 1 threadName=Thread-0
长时间处理任务后的返回的值 2 threadName=Thread-0
end task
长时间处理任务后的返回的值 1 threadName=Thread-1
长时间处理任务后的返回的值 2 threadName=Thread-1
end task
耗时: 3

通过运行结果可以看出耗时变成了3S. 当一个线程访问object的一个snychronized同步代码块时,另一个线程仍然可以访问该object对象中的非synchronized(this)同步代码块.

3. 多个sync代码块间的同步性

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

    public void serviceMethodB() {
        synchronized (this) {
            System.out.println("B begin time=" + System.currentTimeMillis());
            System.out.println("B end    end=" + System.currentTimeMillis());
        }
    }
}

public class ThreadA extends Thread {

    private ObjectService service;

    public ThreadA(ObjectService service) {
        super();
        this.service = service;
    }
    @Override
    public void run() {
        super.run();
        service.serviceMethodA();
    }
}

public class ThreadB extends Thread {
    private ObjectService service;

    public ThreadB(ObjectService service) {
        super();
        this.service = service;
    }
    @Override
    public void run() {
        super.run();
        service.serviceMethodB();
    }
}

public class Run {

    public static void main(String[] args) {
        ObjectService service = new ObjectService();

        ThreadA a = new ThreadA(service);
        a.setName("a");
        a.start();

        ThreadB b = new ThreadB(service);
        b.setName("b");
        b.start();
    }
}
运行结果:
A begin time=1521628118097
A end    end=1521628120097
B begin time=1521628120098
B end    end=1521628120098
同步运行,另一个被阻塞

当一个线程访问一个对象的sync(this)同步代码块时,其他线程对同一个对象中的其他sync同步代码块的访问将被阻塞.这说明sync使用的是对象监视器

4. 将任意对象作为对象监视器

多个线程调用同一个对象的不同名称的sync同步方法或sync(this)同步代码块时,调用的效果就是按顺序执行,也就是同步,阻塞的。

sync同步方法或sync(this)同步代码块分别有两种作用:

  1. sync同步方法
    1. 对其他sync同步方法或sync(this)同步代码块调用呈阻塞状态。
    2. 同一时间只有一个线程可以执行sync同步方法中的代码。
  2. sync(this)同步代码块
    1. 对其他sync同步方法或sync(this)同步代码块调用呈阻塞状态。
    2. 同一时间只有一个线程可以执行sync(this)同步代码块中的代码。

根据前面对sync(this)同步代码块的作用总结可知,sycn(非this对象)格式的作用只有一种:sync(非this对象x)同步代码块:

  1. 当多个线程持有对象监视器为同一个对象的前提下,同一时间只有一个线程可以执行sync(this对象x)同步代码块中的代码
  2. 当持有对象监视器为同一个对象的前提下,同一时间只有一个线程可以执行sync(非this对象x)同步代码块中的代码

5. 细化验证3个结论

sync(非this对象x)格式的写法是将x对象本身作为 对象监视器 这样就可以得出以下3个结论:

  1. 当多个线程同时执行sync(x){}同步代码块时呈同步效果
  2. 当其他线程执行x对象中的sync同步方法时呈同步效果
  3. 当其他线程执行x对象方法里面的sync(this)代码块时也呈现同步效果

(1) 验证第一个结论

当多个线程同时执行sync(x){}同步代码块时呈同步效果

public class MyObject {
}

public class ThreadA extends Thread {
    private Service service;
    private MyObject object;

    public ThreadA(Service service, MyObject object) {
        super();
        this.service = service;
        this.object = object;
    }

    @Override
    public void run() {
        super.run();
        service.testMethod1(object);
    }
}

public class ThreadB extends Thread {
    private Service service;
    private MyObject object;

    public ThreadB(Service service, MyObject object) {
        super();
        this.service = service;
        this.object = object;
    }

    @Override
    public void run() {
        super.run();
        service.testMethod1(object);
    }
}

public class Run1_1 {
    public static void main(String[] args) {
        Service service = new Service();
        MyObject object = new MyObject();

        ThreadA a = new ThreadA(service, object);
        a.setName("a");
        a.start();

        ThreadB b = new ThreadB(service, object);
        b.setName("b");
        b.start();
    }
}

public class Service {
    public void testMethod1(MyObject object) {
        synchronized (object) {
            try {
                System.out.println("testMethod1 ____getLock time="
                        + System.currentTimeMillis() + " run ThreadName="
                        + Thread.currentThread().getName());
                Thread.sleep(2000);
                System.out.println("testMethod1 releaseLock time="
                        + System.currentTimeMillis() + " run ThreadName="
                        + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
运行效果:
testMethod1 ____getLock time=1523523486828 run ThreadName=a
testMethod1 releaseLock time=1523523488828 run ThreadName=a
testMethod1 ____getLock time=1523523488829 run ThreadName=b
testMethod1 releaseLock time=1523523490829 run ThreadName=b
由结果可看到同一个对象监视器, 结果同步输出

换不同的对象监视器

public class Run1_2 {
    public static void main(String[] args) {
        Service service = new Service();
        MyObject object1 = new MyObject();
        MyObject object2 = new MyObject();

        ThreadA a = new ThreadA(service, object1);
        a.setName("a");
        a.start();

        ThreadB b = new ThreadB(service, object2);
        b.setName("b");
        b.start();
    }
}
运行结果如下:
testMethod1 ____getLock time=1523523808562 run ThreadName=a
testMethod1 ____getLock time=1523523808564 run ThreadName=b
testMethod1 releaseLock time=1523523810582 run ThreadName=a
testMethod1 releaseLock time=1523523810637 run ThreadName=b
结果异步输出

(2) 验证第二个结论

当其他线程执行x对象中的sync同步方法时呈同步效果

public class MyObject {
    synchronized public void speedPrintString() {
        System.out.println("speedPrintString ____getLock time="
                + System.currentTimeMillis() + " run ThreadName="
                + Thread.currentThread().getName());
        System.out.println("-----------------");
        System.out.println("speedPrintString releaseLock time="
                + System.currentTimeMillis() + " run ThreadName="
                + Thread.currentThread().getName());
    }
}	

public class ThreadA extends Thread {
    private Service service;
    private MyObject object;

    public ThreadA(Service service, MyObject object) {
        super();
        this.service = service;
        this.object = object;
    }

    @Override
    public void run() {
        super.run();
        service.testMethod1(object);
    }
}

public class ThreadB extends Thread {
    private MyObject object;

    public ThreadB(MyObject object) {
        super();
        this.object = object;
    }

    @Override
    public void run() {
        super.run();
        object.speedPrintString();
    }
}

public class Service {
    public void testMethod1(MyObject object) {
        synchronized (object) {
            try {
                System.out.println("testMethod1 ____getLock time="
                        + System.currentTimeMillis() + " run ThreadName="
                        + Thread.currentThread().getName());
                Thread.sleep(5000);
                System.out.println("testMethod1 releaseLock time="
                        + System.currentTimeMillis() + " run ThreadName="
                        + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Run {
    public static void main(String[] args) throws InterruptedException {
        Service service = new Service();
        MyObject object = new MyObject();

        ThreadA a = new ThreadA(service, object);
        a.setName("a");
        a.start();

        Thread.sleep(100);

        ThreadB b = new ThreadB(object);
        b.setName("b");
        b.start();
    }
}
运行结果如下:
testMethod1 ____getLock time=1523524079664 run ThreadName=a
testMethod1 releaseLock time=1523524084664 run ThreadName=a
speedPrintString ____getLock time=1523524084664 run ThreadName=b
-----------------
speedPrintString releaseLock time=1523524084664 run ThreadName=b

(3) 验证第3个结论

当其他线程执行x对象方法里面的sync(this)代码块时也呈现同步效果

只需要把上述项目中的MyObject替换一下即可

public class MyObject {
    public void speedPrintString() {
        synchronized (this) {
            System.out.println("speedPrintString ____getLock time="
                    + System.currentTimeMillis() + " run ThreadName="
                    + Thread.currentThread().getName());
            System.out.println("-----------------");
            System.out.println("speedPrintString releaseLock time="
                    + System.currentTimeMillis() + " run ThreadName="
                    + Thread.currentThread().getName());
        }
    }
}
运行结果如下:
testMethod1 ____getLock time=1523524445528 run ThreadName=a
testMethod1 releaseLock time=1523524450529 run ThreadName=a
speedPrintString ____getLock time=1523524450529 run ThreadName=b
-----------------
speedPrintString releaseLock time=1523524450529 run ThreadName=b
同步输出

6. 静态同步sync方法与sync(class)代码块

(1) 静态同步方法sync

关键字sync还可以应用在static静态方法上,如果这样写,那就是对当前*.java文件对应的Class类进行持锁

public class ThreadA extends Thread {
    @Override
    public void run() {
        Service.printA();
    }
}

public class ThreadB extends Thread {
    @Override
    public void run() {
        Service.printB();
    }
}

public 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");
    }
}
public class Run {
    public static void main(String[] args) {
        ThreadA a = new ThreadA();
        a.setName("A");
        a.start();

        ThreadB b = new ThreadB();
        b.setName("B");
        b.start();
    }
}
运行结果如下:
线程名称为: A 在 1523525481258 进入printA
线程名称为: A 在 1523525484259 离开printA
线程名称为: B 在 1523525484259 进入printB
线程名称为: B 在 1523525484259 离开printB

结论:都是同步的效果,和将sync关键字加到非static 方法上的使用效果是一样的。还是有本质的区别,sync加到static静态方法上是给class类上锁,而sync关键字加到static静态方法上是给对象上锁

例子略,

(2) 验证class类锁,和对象锁无关

public 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() + "离开rintA");
        } 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");
    }
}

public class ThreadA extends Thread {
    private Service service;
    public ThreadA(Service service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.printA();
    }
}

public class ThreadB extends Thread {
    private Service service;
    public ThreadB(Service service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.printB();
    }
}

public class Run {
    public static void main(String[] args) {
        Service service1 = new Service();
        Service service2 = new Service();

        ThreadA a = new ThreadA(service1);
        a.setName("A");
        a.start();

        ThreadB b = new ThreadB(service2);
        b.setName("B");
        b.start();
    }
}
结论:虽说是不同的对象,但sync加在静态方法上是对class上锁,所以还是同步执行。

(3) sync(class)代码和sync static 方法作用一样

public class Service {
    public static void printA() {
        synchronized (Service.class) {
            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();
            }
        }
    }

    public static void printB() {
        synchronized (Service.class) {
            System.out.println("线程名称为:" + Thread.currentThread().getName()
                    + " 在 " + System.currentTimeMillis() + "进入printB");
            System.out.println("线程名称为:" + Thread.currentThread().getName()
                    + " 在 " + System.currentTimeMillis() + "离开printB");
        }
    }
}

public class ThreadA extends Thread {
    private Service service;

    public ThreadA(Service service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.printA();
    }
}

public class ThreadB extends Thread {
    private Service service;

    public ThreadB(Service service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.printB();
    }
}

public class Run {
    public static void main(String[] args) {
        Service service1 = new Service();
        Service service2 = new Service();

        ThreadA a = new ThreadA(service1);
        a.setName("A");
        a.start();

        ThreadB b = new ThreadB(service2);
        b.setName("B");
        b.start();
    }
}