synchronized同步方法的弊端和解决方法(同步代码块)

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

当两个或多个并发线程访问同一个对象中的synchronsized方法(或代码块)时,一段时间内只允许一个线程访问,所以其他线程必须等待当前线程执行完才能执行。

话不多说看示例:

synchronized同步代码块示例

Task.java

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

CommonUtils.java

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

Thread1.java

public class Thread1 extends Thread{
    private Task task;
    public Thread1(Task task){
        super();
        this.task = task;
    }

    @Override
    public void run() {
        super.run();
        CommonUtils.beginTime1 = System.currentTimeMillis();
        task.doLongTimeTask();
        CommonUtils.endTime1 = System.currentTimeMillis();
    }
}

Thread2.java

public class Thread2 extends Thread{
    private Task task;
    public Thread2(Task task){
        super();
        this.task = task;
    }

    @Override
    public void run() {
        super.run();
        CommonUtils.beginTime2 = System.currentTimeMillis();
        task.doLongTimeTask();
        CommonUtils.endTime2 = System.currentTimeMillis();
    }
}

Run.java

public class Run {
    public static void main(String[] args) {
        Task task = new Task();
        Thread1 t1 = new Thread1(task);
        t1.start();
        Thread2 t2 = new Thread2(task);
        t2.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) + "s");
    }
}

输出结果:

begin Task
长时间处理任务后从远程返回的值 1 threadName = Thread-1
长时间处理任务后从远程返回的值 1 threadName = Thread-1
end Task
begin Task
长时间处理任务后从远程返回的值 1 threadName = Thread-0
长时间处理任务后从远程返回的值 1 threadName = Thread-0
end Task
耗时:6s

 

很明显的效率问题,接下来试下用同步代码块的方式解决这个问题。

修改Task.java

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 = "长时间处理任务后从远程返回的值 1 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){
            e.printStackTrace();
        }
    }
}

输出结果:

begin Task
begin Task
长时间处理任务后从远程返回的值 1 threadName = Thread-1
长时间处理任务后从远程返回的值 1 threadName = Thread-0
end Task
长时间处理任务后从远程返回的值 1 threadName = Thread-0
长时间处理任务后从远程返回的值 1 threadName = Thread-0
end Task
耗时:3s

 

效率提高了一倍,可以看出除了同步代码块部分,其余部分都是异步执行的。

第二个线程可以在第一个线程还没完全执行完时使用该方法而不需要等待第一个线程完全执行完。

 

synchronized 代码块 对象监视器

上述例子的对象监视器是 this,也就是第一个线程执行时 会取得 this锁,直到执行完成释放,其他线程才能获得 this 锁。接下来试下 其他对象作为对象监视器的效果。

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

看示例:

MyObject.java

public class MyObject {

}

Service.java

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

MyThread1.java

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

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

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

MyThread2.java

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

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

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

Run1_1.java

public class Run1_1 {
    public static void main(String[] args) {
        Service service = new Service();
        MyObject object = new MyObject();
        MyThread1 myThread1 = new MyThread1(service,object);
        myThread1.start();
        MyThread2 myThread2 = new MyThread2(service,object);
        myThread2.start();
    }
}

输出结果:

testMethod1 getLock time = 1515642178888 run ThreadName = Thread-0
testMethod1 releaseLock time = 1515642180890 run ThreadName = Thread-0
testMethod1 getLock time = 1515642180890 run ThreadName = Thread-1
testMethod1 releaseLock time = 1515642182890 run ThreadName = Thread-1

说明是同步的,第一个线程执行完后释放object锁,第二个线程才能开始

如过是不同对象,则能异步输出

Run1_2.java

public class Run1_2 {
    public static void main(String[] args) {
        Service service = new Service();
        MyObject object = new MyObject();
        MyObject object2 = new MyObject();
        MyThread1 myThread1 = new MyThread1(service,object);
        myThread1.start();
        MyThread2 myThread2 = new MyThread2(service,object2);
        myThread2.start();
    }
}

输出结果:

testMethod1 getLock time = 1515642274410 run ThreadName = Thread-0
testMethod1 getLock time = 1515642274410 run ThreadName = Thread-1
testMethod1 releaseLock time = 1515642276411 run ThreadName = Thread-1
testMethod1 releaseLock time = 1515642276411 run ThreadName = Thread-0

可以看出两个线程交替输出,说明锁的不是同一个对象。

 

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

示例:

MyObject.java

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

MyThread1.java

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

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

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

MyThread2.java

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

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

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

Run1_3.java

public class Run1_3 {
    public static void main(String[] args) {
        Service service = new Service();
        MyObject object = new MyObject();
        MyThread1 myThread1 = new MyThread1(service,object);
        myThread1.start();
        MyThread2 myThread2 = new MyThread2(service,object);
        myThread2.start();
    }
}

输出结果:

testMethod1 getLock time = 1515654017965 run ThreadName = Thread-0
testMethod1 releaseLock time = 1515654022965 run ThreadName = Thread-0
PrintTimeAndthreadName getLock time = 1515654022965 run ThreadName = Thread-1
----------------------------------------
PrintTimeAndthreadName relseaseLock time = 1515654022965 run ThreadName = Thread-1

 

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

修改MyObject.java

public class MyObject {
    public void PrintTimeAndthreadName() {
        synchronized (this) {
            System.out.println("PrintTimeAndthreadName getLock time = " + System.currentTimeMillis() + " run ThreadName = " + Thread.currentThread().getName());
            System.out.println("----------------------------------------");
            System.out.println("PrintTimeAndthreadName relseaseLock time = " + System.currentTimeMillis() + " run ThreadName = " + Thread.currentThread().getName());
        }
    }
}

Run1_3.java 输出结果不变。

 

转载于:https://my.oschina.net/xiaozhiwen/blog/1605341

你可能感兴趣的:(synchronized同步方法的弊端和解决方法(同步代码块))