Rumah  >  Artikel  >  Java  >  学习篇:关于Java中的同步

学习篇:关于Java中的同步

php是最好的语言
php是最好的语言asal
2018-08-09 16:48:442057semak imbas

在我们平常的开发工作中,或多或少的都能接触到多线程编程或者一些并发问题,随着操作系统和系统硬件的升级,并发编程被越来越多的运用到我们的开发中,我们使用多线程的最初的想法是能够更大程度的利用系统资源,但是我们在使用多线程的时候,也会有一些问题的存在,我们先来看一段代码。

    private static int i = 0;

    private static void increse(){
        i++;
    }

    public static void main(String[] args) {
    Thread[] threads = new Thread[20];
        for (int i = 0; i < threads.length; i++){
            threads[i] = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 10000; j++){
                        increse();
                    }
                }
            });
            threads[i].start();
        }
        
        while (Thread.activeCount() > 1){
            Thread.yield();
        }
        
        System.out.println(i);
    }

首先看看这段代码是没有问题的,但是如果在多线程的环境中,这段代码运行的结果基本是都不一样的,这里是开启20个线程,然后每一个线程调用increse()方法对变量i进行一个赋值操作,预期的一个输出应该是200000,但是为什么会每一次的输出都不太一样呢?原因就在于这个地方i++,这里就是产生并发问题的根本原因,那看起来很简单的一个i++为什么会有这种问题?这里就简单的从java内存模型(JMM)来了解一下,首先JMM规定,每一个线程运行时都会有一个工作内存,然后变量i是存储在主内存的,每次线程在计算数据的时候都要去主内存中获取当前变量的值,那么简单的来说,就是一个线程将变量i计算得到结果后,还没有将这个数据刷新到主内存,在这个时候,其他的线程已经获取到了原来的值,换句话说,本线程中获取到的数据是否是最新的,这个是不知道的。但是这只是从JMM角度来简单的说一下,i++这个看似简单的操作其实包含了三个操作,获取i的值,对i进行自增,然后对i进行赋值。就是在这几个操作中,其他的线程会有很多的时间来做很多的事情,那有人会问,是不是将这个i++操作同步就可以了?是的,那该如何同步呢?

有人说用volatile来修饰一下变量i不就可以了么?是的,javavolatile这个关键字确实是提供了一个同步的功能,但是为什么在这里修改一下还是没有效果呢?原因就在于如果一个变量用volatile修饰之后,只是会让其他的线程立即能够知道当前变量的值是多少,这里就叫做可见性,但是还是解决不了i++这几个操作的问题,那如何处理,又有人提出用synchronized这个关键字,不得不承认,这个关键字确实很强大,是能够解决这个问题,那我们有没有考虑过这个为什么可以解决这个问题,是如何解决的,那还是简单的说一下,首先synchronized是属于JVM级别的,有这个关键字的方法或者代码块,最后会被解释成monitorentermonitorexit指令,这两个字节码都明确需要一个reference类型的参数来指出要锁定或者解锁的对象,像这样:

public synchronized String f(){
    //code
}

synchronized(object){
    //code
}

看到这里,我们应该能明白synchronized为什么可以解决上面程序的问题,但是我们还应该要明确一个概念就是原子性,换句话说,就是我们在处理一些多线程的问题的时候,应该保证一些共享数据的操作是原子性的,这样才能保证正确性,看到这里,相信你也有了一个大概的理解,那我们来总结一下,在处理多线程的问题的时候,哪些点是值得注意的,可见性原子性有序性,这几个点是保证多线程能够正确的一个前提条件,至于什么是有序性,这里涉及到内存指令的重排序,不在讨论范围内,以后再来讨论。

这里还要指出一个问题,就是是否我们在处理多线程问题的时候,一定要同步,或者说一定要加锁,这个也不是一定的,之前网上有一个说笑的方式,就是我们在处理多线程的问题的时候,有时候就会发现,代码又被写成了单线程,当然这只是一个玩笑话,但是这里我们也能看出来,是不是单线程的程序就不会有这些问题?答案是肯定的,因为单线程不存在资源竞争的问题,也就不需要再讨论了。

那么我们什么时候需要使用同步,什么时候又不需要呢?我们来看一段代码

    public String f(String s1, String s2, String s3){
        return s1 + s2 +s3;
    }

这是一个字符串拼接的一个方法,我们来反编译看一下,这里JVM到底是怎么做的?

相关推荐:

关于Java线程同步和同步方法的详解

详细介绍Java同步块synchronized block的使用

Atas ialah kandungan terperinci 学习篇:关于Java中的同步. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn