zoukankan      html  css  js  c++  java
  • java synchronized

      网上有许多关于synchronized关键字用法的文章,发现有些文章里介绍的用法和场景,这里我整理一下,通过实践,以一种有别于传统的写法介绍这个关键字的用法!用图文并茂的方式展示出来,希望大家理解起来更加简单易懂。本人知识有限,不足或错误的地方,欢迎指正,谢谢。

    准备个实际测试用的例子

    public class TestProgram extends Thread {
        
        static int j = 0;
        
        public static synchronized void testA(){
            j++;
            System.out.println(Thread.currentThread().getName() + ": " + j);
        }
        
        public synchronized void testB(){
            j++;
            System.out.println(Thread.currentThread().getName() + ": " + j);
        }
        
        public void testC(){
            System.out.println("into testC...");
            synchronized (this) {
                j++;
                System.out.println(Thread.currentThread().getName() + ": " + j);
            }
        }
       
    
        @Override
        public void run() {
            testA();
            testB();
            testC();
        }


    这就是这个例子的所有代码,代码简单。这里简短介绍一下:

      总共有三个测试方法,分别是testA、testB和testC。testA方法是一个加了synchronized关键字和static关键字的方法,testB是只加了synchronized关键字的方法,testC是代码块中包含了synchronized块的方法,这里分别用两种模拟环境测试这三个方法的不同,用来区分三个方法在不同场景下的不同!

    首先是测试环境(两个线程跑同一个program,我在这里只创建了一个TestProgram实例)

    同一实例下测试

    public static void main(String[] args) {
        TestProgram program = new TestProgram();
        Thread t = new Thread(program);
        Thread t2 = new Thread(program);
        t.start();
        t2.start();
    }

    测试testA()

    这里我用两个线程去测试,通过start()调用run()方法,我在testA()方法里打了个断点,然后两个线程跑起来,我们看结果

    我们在debug模式下,用多线程调试看结果,可以看到生成了Thread-1和Thread-2两个线程,在红色标记的代码中,可以看到Thread-1进入了testA()方法,而Thread-2则没有进入到testA()方法,这说明synchronized起到了同步作用。

    接着我让线程Thread-1跑完testA()方法后,Thread-2方法就直接被激活了,直接进入testA()方法,同步有效了。

    得到第一个结论,在同一个实例下,synchronized关键字是对static方法起到同步作用的。

    当Thread-1和Thread-2都跑完testA()方法后输出结果:

    Thread-1: 1
    Thread-2: 2

    为什么这个结论前面要加一个同一个实例?后面会看到,不在同一个实例下的多线程,synchronized关键效果不一样!

    接下来是进入testB()方法

    当Thread-1进入testB()方法时,看Thread-2线程进入了一种叫stepping的模式(类似于等待),在进入这个等待的过程中,如果我们将Thread-1线程跑完testB()方法,那么Thread-2的状态将由stepping改成suspended。简单的说就是当一个线程进入testB()时,其他线程都在等这个线程跑完。

    在这里发现synchronized方法也起到了同步的效果。

    再看测试testC()方法的效果

     我们可以看到,两个线程都进入到了testC()方法,而且两个线程的状态都是suspended。接下来,当线程Thread-1进入到synchronized代码块中,看结果

    可以看到当线程Thread-1进入代码块后,Thread-2再进入这个代码块,状态就变成了stepping等待的情况了。说明这里synchronized方式同步有效。

    最后执行testC()方法后的结果是:

    into testC...
    into testC...
    Thread-1: 5
    Thread-2: 6

    以上就是在同一个实例下跑出来的结果。但如果是这种情况下,情况就不同了,这也是我们经常遇到的问题,有时候感觉怎么与以前的理论相驳,接着分析

    不同实例下的测试

    public static void main(String[] args) {
        TestProgram program = new TestProgram();
        TestProgram program2 = new TestProgram();
        Thread t = new Thread(program);
        Thread t2 = new Thread(program2);
        t.start();
        t2.start();
    }

    换了两行代码,在两个相同的TestProgram中,创建两个对象,与上面的写法差异在两个线程跑各自的实例,这个时候synchronized的作用效果就不太一样了

    首先是testA()方法

    测试的结果是

    Thread-3: 1
    Thread-2: 2

    这个结果和开始测试的结果一样,说明在这里synchronized起到了同步效果!

    测试testB()

    这里结果就和上面测试testB()效果就不一样了,我们看效果

    从图中可以看到,两个线程都同时进入了testB()方法中,也就是synchronized关键字没起到同步两个线程的作用

    这里得出一个结论:两个不同的实例,在不同的线程环境下,synchronized并不能实现同步效果,当然,static方法除外

    后面的testC()方法也一样,看看效果

    两个线程都同时进入了第22行代码,就是同时在synchronized块中,说明这个synchronized代码块没有起到同步的效果。

    综合以上所诉,我得出一个这样的结论:

    如果多线程是对同一个对象的引用,那么synchronized关键字在所有的情况下都有同步的效果,如果多线程是对同一个对象的不同引用(创建了多个对象),除了static方法上面加的synchronized方法有同步引用的效果外,其他的synchronized(包括synchronized代码块)将没有同步的效果。

    至于为什么static方法上加synchronized方法,在不同的场景下都有效,原因是:被static修饰的成员变量和成员方法独立于该类的任何对象,static方法不属于类创建的引用,所以无论创建了多少个对象,对于static方法而言,都跟它没关系。

    ps:synchronized(this)这个this就是指对象的引用,这里指我当前线程的对象引用。

    我的疑问:为什么创建一个对象调试的时候,是Thread-1和Thread-2,而我创建两个对象调试的时候却是Thread-2和Thread-3?

  • 相关阅读:
    CSS中position小解
    position
    mac默认安装postgresql, 如何让postgresql可以远程访问
    The data directory was initialized by PostgreSQL version 9.6, which is not compatible with this version 10.0.
    active admin gem error
    psql 无法添加超级用户
    ubuntu 15.04 安装Balsamiq Mockups 3
    Rails html 写public里图片的路径
    rails c 历史命令
    undefined local variable or method `per' for []:ActiveRecord::Relation
  • 原文地址:https://www.cnblogs.com/wxwall/p/3776414.html
Copyright © 2011-2022 走看看