Java中synchronized的使用

贴一下关于synchronized的帖子:https://www.jianshu.com/p/d53bf830fa09
PS:个人觉得写的非常不错,非常推荐阅读,有助于对多线程以及JMM的理解。

下面是我对synchronized的使用测试:

一、synchronized(this)

说明:synchronized用来同步自身对象
这个用法估计是很多初学者经常看到的用法(说的我自己好像不是初学者似的)。
下面就来猜下以下程序的执行结果吧。

执行类MainTest:

public class MainTest {

    public static void main(String[] args) throws InterruptedException {
        // 创建对象o1,o2
        TestObject o1 = new TestObject();
        TestObject o2 = new TestObject();

        // 创建线程t1,t2
        ThreadTest t1 = new ThreadTest(o1);
        ThreadTest t2 = new ThreadTest(o2); // TODO 将o2替换成o1试试看

        t1.start();
        t2.start();

        // 等待线程t1,t2执行完成
        t1.join();
        t2.join();

        // t1,t2都执行完成之后,最终输出结果
        System.out.println(TestObject.i);
    }
}

测试对象类ObjectTest:

public class ObjectTest {
    public static int i = 0;

    /**
     * this
     */
    public void add() {
        synchronized (this) {
            for (int k = 0; k < 100000; k++) {
                i++;
            }
        }
    }
}

线程实现类ThreadTest:

public class ThreadTest extends Thread {

    private ObjectTest objectTest;

    public ThreadTest(ObjectTest objectTest) {
        this.objectTest = objectTest;
    }

    @Override
    public void run() {
        // this
        objectTest.add();
    }
}

运行结果:多次运行执行类之后,发现最终的结果值始终<200000;按照TODO里操作后,最终值都=200000。

二、synchronized method

说明:synchronized用来声明方法。
执行类和线程实现类不动,只修改测试对象类,继续猜猜执行结果吧。

测试对象类ObjectTest:

public class ObjectTest {
    public static int i = 0;

    // TODO 按照一里的TODO修改试试看
    public synchronized void add() {
        for (int k = 0; k < 100000; k++) {
            i++;
        }
    }
}

运行结果:多次运行之后,最终结果依然是<200000。按照TODO修改之后,最终值都=200000。

三、synchronized(class)

说明:synchronized用来同步类的对象类,即Class<T> clazz = T.class中的clazz,描述类对象信息的。
执行类和线程实现类不动,只修改测试对象类,继续猜猜执行结果吧。

测试对象类ObjectTest:

public class ObjectTest {
    public static int i = 0;

    public void add() {
        synchronized (ObjectTest.class) {
            for (int k = 0; k < 100000; k++) {
                i++;
            }
        }
    }
}

运行结果:多次运行之后,运行结果都=200000。

四、static method synchronized代码块

说明:静态方法中使用synchronized代码块
执行类和线程实现类不动,只修改测试对象类。

测试对象类ObjectTest:

public class ObjectTest {
    public static int i = 0;

    public static void add() {
        synchronized (ObjectTest.class) {
            for (int k = 0; k < 100000; k++) {
                i++;
            }
        }
    }
}

运行结果:多次运行之后,运行结果都=200000。

五、static synchronized method

说明:synchronized用来声明静态方法
执行类和线程实现类不动,只修改测试对象类。

测试对象类ObjectTest:

public class ObjectTest {
    public static int i = 0;

    public static synchronized void add6() {
        for (int k = 0; k < 100000; k++) {
            i++;
        }
    }
}

运行结果:多次运行之后,运行结果都=200000。

六、synchronized(object)

说明:synchronized同步对象,其实这个本质和一是一样的,但是同步对象有区别。我下面例子直接用String来替代。

线程实现类不动,修改测试对象类,执行类。

测试对象类ObjectTest:

public class ObjectTest {
    public static int i = 0;
    private String key;

    /**
     * 锁key
     */
    public void add() {
        String key = this.key;
        synchronized (key) {
            for (int k = 0; k < 100000; k++) {
                i++;
            }
        }
    }

    // Getters And Setters
    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }
}

执行类MainTest:

public class MainTest {

    public static void main(String[] args) throws InterruptedException {
        // 创建对象o1,o2
        ObjectTest o1 = new ObjectTest();
        o1.setKey("A");
        ObjectTest o2 = new ObjectTest();
        o2.setKey("A"); // TODO 修改B试试看

        // 创建线程t1,t2
        ThreadTest t1 = new ThreadTest(o1);
        ThreadTest t2 = new ThreadTest(o2);

        t1.start();
        t2.start();

        // 等待线程t1,t2执行完成
        t1.join();
        t2.join();

        // t1,t2都执行完成之后,最终输出结果
        System.out.println(ObjectTest.i);
    }
}

运行结果:多次运行之后,运行结果都=200000;按照TODO修改之后,多次运行结果都<200000。

七、总结

一句话总结:修改同一对象时,多个线程访问同一方法,并且取得相同的锁,这时候是线程安全的。

【一】里面两个线程虽然访问同一个方法,但是取得的锁却不是同一个,因为o1,o2不是同一个对象,所以这时候线程是不安全的。改成TODO里的写法之后,取得的锁是同一对象,所以这时候线程是安全的。

【二】、【六】的道理和【一】是一样的。

【三】两个线程访问同一个方法,获取的锁是类的类对象,一个类对象就是对类本身的描述,这个肯定是相同的,所以也是线程安全的。

【四】、【五】的道理和【三】一样。

最后,如果多线程访问通过一个对象,并修改该对象,要保证线程安全,其中一种实现手法是:对该对象加锁,即当一个线程拿到该对象锁之后,其它线程要等待该线程释放锁之后,才能继续操作。这个实现手法延伸到分布式开发,也是一样的,只不过这个时候需要协调程序来进行协调了,比如zookeeper的分布式锁。还有一种实现手法是:CAS,ConcurrentHashMap里,进行扩容的时候,好像就有用的这种实现手法。对于线程安全,我知道的就这两种实现手法,而且我只是大概的了解它们,并没有实际的去实现过,哪怕是简单的实现也没有,之后会尝试简单的去实现它们。

注:上面的纯属个人理解,包括那句总结的话。理解不对或有疑问的地方,请多多指教。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值