java多线程:synchronized的深度理解

背景

在上篇文章java多线程:volatile的深度理解文末,提到了volatile与synchronized的主要区别:

  • volatile关键字解决的是内存可见性的问题
  • synchronized关键字解决的是执行控制的问题

该篇文章继续对java多线程:synchronized的应用进行解析。

顺序一致型内存模型

所有线程都只能看到一个单一的操作执行顺序。在顺序一致性内存模型中,每个操作都必须原子执行且立刻对所有线程可见
可以用以下示意图来理解这个模型:

内存通过一个左右摆动的开关可以连接到任意一个线程,在任意时间点最多只能有一个线程可以连接到内存。当多个线程并发执行时,图中的开关装置能把所有线程的所有内存读/写操作串行化。

在这里插入图片描述

synchronized的原理

查看以下代码:

/**
 * synchronized内存一致性模型
 *
 * @author zhuhuix
 * @date 2020-05-07
 */
public class SynchronizedThread implements Runnable {
    int v = 0;

    public synchronized void Increment() {
        v++;
        System.out.println("线程:" + Thread.currentThread().getName() + "获取的静态值为:" + v);
    }

    @Override
    public void run() {
        Increment();
    }

    public static void main(String[] args) {

        SynchronizedThread synchronizedThread = new SynchronizedThread();
        for (int i = 0; i < 50; i++) {
            new Thread(synchronizedThread).start();

        }

    }
}

synchronized 的加锁操作,实际上变成了串行程序:
在这里插入图片描述
等同于以下代码:而且多线程程序频繁的加锁释放锁,导致执行效率还不如单线程

/**
 * 单线程运行
 *
 * @author zhuhuix
 * @date 2020-05-07
 */
public class Serial {
    int v = 0;

    public void Increment() {
        v++;
        System.out.println("获取的静态值为:" + v);
    }

    public static void main(String[] args) {
        Serial serial = new Serial();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        Long start =System.currentTimeMillis();
        System.out.println(sdf.format(start));
        for (int i = 0; i < 50; i++) {

            serial.Increment();
        }
        Long end = System.currentTimeMillis();
        System.out.println(sdf.format(end));

        System.out.println(end-start);
    }
}

synchronized的实例解析

首先我们来看一段单线程代码:该段程序实现了对四个随机打乱的list做了冒泡排序,在排序过程中,对静态变量k和v进行了相加计算。

代码段1
/**
 * 单线程冒泡排序
 *
 * @author zhuhuix
 * @date 2020-05-07
 */
public class SerialBubble {
    public static Long k=0L;
    public static Long v=0L;

    public static void main(String[] args) {

        List<Integer> list1 = new ArrayList<>();
        List<Integer> list2 = new ArrayList<>();
        List<Integer> list3 = new ArrayList<>();
        List<Integer> list4 = new ArrayList<>();

        for (int i = 1; i < 10000; i++) {
            list1.add(i);
            list2.add(i);
            list3.add(i);
            list4.add(i);
        }
        //随机打乱list1
        Collections.shuffle(list1);
        //随机打乱list2
        Collections.shuffle(list2);
        //随机打乱list3
        Collections.shuffle(list3);
        //随机打乱list4
        Collections.shuffle(list4);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        Long start = System.currentTimeMillis();
        System.out.println("单线程程序开始时间:" + sdf.format(start));

        List<Integer> list1Sort = bubbleSort((list1));
        List<Integer> list2Sort = bubbleSort((list2));
        List<Integer> list3Sort = bubbleSort((list3));
        List<Integer> list4Sort = bubbleSort((list4));


        Long end = System.currentTimeMillis();
        System.out.println("单线程程序结束时间:" + sdf.format(end));

        System.out.println("排序耗用了" + (end - start) + "毫秒" + ";最终v的值=" + v);

    }

    public static List<Integer> bubbleSort(List<Integer> list) {
        for (int i = 0; i < list.size() - 1; i++) {
            for (int j = i + 1; j < list.size(); j++) {
                if (list.get(i) > list.get(j)) {
                    int temp = list.get(i);
                    list.set(i, list.get(j));
                    list.set(j, temp);
                }
            }
            add();
        }
        return list;
    }

    public static  void add() {
        for (int i = 0; i < 100; i++) {
            k=k+i;
            v=v+k;
        }
    }

}

运行结果:由于是单线程程序,k和v的值不存在线程共享与同步的问题,是安全的。
在这里插入图片描述

代码段2

为了加快程序的运行效果,对时间复杂度较高的冒泡算法,希望通过多线程运行,但运行过程中由于存在静态变量k和v的相加计算,必须要通过给对象的方法加上锁,防止线程不同步。

/**
 * 多线程冒泡排序
 *
 * @author zhuhuix
 * @date 2020-05-07
 */
public class ThreadBubble implements Runnable {
    public static Long k=0L;
    public static Long v=0L;


    private List<Integer> list;

    ThreadBubble(List<Integer> list) {
        this.list = list;
    }

    public List<Integer> getList() {
        return list;
    }

    /**
     * 通过synchronized static 给静态变量上锁
     */
    public static synchronized  void add() {
        for (int i = 0; i < 100; i++) {
           k=k+i;
           v=v+k;
        }
    }


    @Override
    public void run() {
        for (int i = 0; i < list.size() - 1; i++) {
            for (int j = i + 1; j < list.size(); j++) {
                if (list.get(i) > list.get(j)) {
                    int temp = list.get(i);
                    list.set(i, list.get(j));
                    list.set(j, temp);
                }
            }
            add();
        }
    }


    public static void main(String[] args) {

        List<Integer> list1 = new ArrayList<>();
        List<Integer> list2 = new ArrayList<>();
        List<Integer> list3 = new ArrayList<>();
        List<Integer> list4 = new ArrayList<>();

        for (int i = 1; i < 10000; i++) {
            list1.add(i);
            list2.add(i);
            list3.add(i);
            list4.add(i);
        }

        //随机打乱list1
        Collections.shuffle(list1);
        //随机打乱list2
        Collections.shuffle(list2);
        //随机打乱list3
        Collections.shuffle(list3);
        //随机打乱list4
        Collections.shuffle(list4);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        Long start = System.currentTimeMillis();
        System.out.println("多线程程序开始时间:" + sdf.format(start));

        ThreadBubble bubbleSort1 = new ThreadBubble(list1);
        Thread thread1 = new Thread(bubbleSort1);
        thread1.start();

        ThreadBubble bubbleSort2 = new ThreadBubble(list2);
        Thread thread2 = new Thread(bubbleSort2);
        thread2.start();

        ThreadBubble bubbleSort3 = new ThreadBubble(list3);
        Thread thread3 = new Thread(bubbleSort3);
        thread3.start();

        ThreadBubble bubbleSort4 = new ThreadBubble(list4);
        Thread thread4 = new Thread(bubbleSort4);
        thread4.start();

        //四个线程都结束了,主程序才结束
        while (thread1.isAlive() || thread2.isAlive() || thread3.isAlive() || thread4.isAlive()) {
        }

        Long end = System.currentTimeMillis();
        System.out.println("多线程程序结束时间:" + sdf.format(end));
        System.out.println("排序耗用了" + (end - start) + "毫秒"+";v的最终值为"+v );

    }

}

运行结果如下:多线程在加快运算速度的前提下,通过synchronized锁的修饰保证了共享变量的同步。
在这里插入图片描述

总结

关键字synchronized可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性。
注意:

  • 每个对象都可以做为锁,但一个对象做为锁时,应该被多个线程共享,这样才显得有意义,在并发环境下,一个没有共享的对象作为锁是没有意义的。
  • 无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁。
  • 如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法 。
  • 但不同的对象实例的 synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

智慧zhuhuix

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值