多线程之synchronized同步语句块(1)

本文深入探讨了Java中使用synchronized关键字声明方法的弊端,并通过示例代码展示了如何利用synchronized同步代码块来优化线程同步,实现部分同步、部分异步的执行效果,从而提高程序的执行效率。
摘要由CSDN通过智能技术生成

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

1.synchronized方法的弊端

我们来证明一下synchronized关键字声明方法是有弊端的。
代码示例:
方法类:

package test;

/**
 * @Author LiBinquan
 */
public class Task {
    private String getData1;
    private String getData2;
    public synchronized void doLongTimeTask(){
        try{
            System.out.println("开始 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");
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

工具类:

package test;

/**
 * @Author LiBinquan
 */
public class CommonUtils {
    public static long beginTime1;
    public static long endTime1;
    public static long beginTime2;
    public static long endTime2;

}

线程1:

package test;


/**
 * @Author LiBinquan
 */
public class ThreadTest1 extends Thread{
    private Task task;
    public  ThreadTest1(Task task){
        super();
        this.task = task;
    }

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

线程2:

package test;


/**
 * @Author LiBinquan
 */
public class ThreadTest2 extends Thread{
    private Task task;
    public  ThreadTest2(Task task){
        super();
        this.task = task;
    }

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

运行类:

package test;

/**
 * @Author LiBinquan
 */
public class Run {
    public static void main(String[] args) throws InterruptedException {
        Task task = new Task();
        ThreadTest1 threadTest1 = new ThreadTest1(task);
        threadTest1.start();
        ThreadTest2 threadTest2 = new ThreadTest2(task);
        threadTest2.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关键字俩声明方法 public synchronized void doLongTimeTask()时从运行的时间上来看,弊端很明显,解决这样的问题可以使用synchronized同步块。

2.synchronized同步代码块的使用

当两个并发线程访问同一个对象object中的synchronized(this)同步代码块时,一段时间内只能有一个线程被执行,另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
示例代码:
方法类:

package test;

/**
 * @Author LiBinquan
 */
public class ObjectService {
    public void serviceMethod(){
        try{
            synchronized (this){
                System.out.println("begin beginTime = "+System.currentTimeMillis());
                Thread.sleep(2000);
                System.out.println("end     endTime = "+System.currentTimeMillis());
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

线程1:

package test;


/**
 * @Author LiBinquan
 */
public class ThreadTest1 extends Thread{
    private ObjectService service;
    public ThreadTest1(ObjectService service){
        super();
        this.service = service;
    }

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

线程2:

package test;


/**
 * @Author LiBinquan
 */
public class ThreadTest2 extends Thread{
    private ObjectService service;
    public ThreadTest2(ObjectService service){
        super();
        this.service = service;
    }

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

运行类:

package test;

/**
 * @Author LiBinquan
 */
public class Run {
    public static void main(String[] args) throws InterruptedException {
        ObjectService service = new ObjectService();
        ThreadTest1 threadTest1 = new ThreadTest1(service);
        threadTest1.setName("A");
        threadTest1.start();
        ThreadTest2 threadTest2 = new ThreadTest2(service);
        threadTest2.setName("B");
        threadTest2.start();
    }
}

输出:
在这里插入图片描述
虽然这个示例中我们使用了synchronized同步代码块,但执行的效率还是没有提高,执行的效果还是同步运行的。

3.同步代码块来解决同步方法的弊端

这边修改一下 上面的方法就行,代码如下:

package test;

/**
 * @Author LiBinquan
 */
public class Task {
    private String getData1;
    private String getData2;
    public void doLongTimeTask(){
        try{
            System.out.println("开始 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");
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

输出:
在这里插入图片描述
通过上面的测试可以得知,当一个线程访问object的一个synchronized同步代码块时,另一个线程仍然可以访问该object对象中的非synchronized(this)同步代码块。
这边我们可以看到运行时间缩短但是同步synchronized代码块真的是同步的吗?真的持有当前调用对象的锁吗?答案为:是 ,这边我们就需要验证一下。

4.一半异步,一半同步

这边我们要证明一点:不在synchronized块中就是异步执行,在synchronized块中就是同步执行。
方法类:

package test;

/**
 * @Author LiBinquan
 */
public class Task {
    public void doLongTimeTask(){
        for (int i = 0; i < 100; i++) {
            System.out.println("非同步代码块 线程名 = "+Thread.currentThread().getName()+ " i = "+(i +1));
        }
        synchronized (this){
            for (int i = 0; i < 100; i++) {
                System.out.println("同步代码块  线程名 = "+Thread.currentThread().getName()+ " i = "+(i +1));
            }        
        }
    }
}

线程1:

package test;


/**
 * @Author LiBinquan
 */
public class ThreadTest1 extends Thread{
    private Task task;
    public  ThreadTest1(Task task){
        super();
        this.task = task;
    }

    @Override
    public void run() {
        super.run();
        task.doLongTimeTask();
    }
}

线程2:

package test;


/**
 * @Author LiBinquan
 */
public class ThreadTest2 extends Thread{
    private Task task;
    public  ThreadTest2(Task task){
        super();
        this.task = task;
    }

    @Override
    public void run() {
        super.run();
        task.doLongTimeTask();
    }
}

运行类:

package test;

/**
 * @Author LiBinquan
 */
public class Run {
    public static void main(String[] args) throws InterruptedException {
        Task task = new Task();
        ThreadTest1 threadTest1 = new ThreadTest1(task);
        threadTest1.start();
        ThreadTest2 threadTest2 = new ThreadTest2(task);
        threadTest2.start();
    }
}

输出:
在这里插入图片描述
非同步代码块交叉打印

在这里插入图片描述
同步代码块排队执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值