zoukankan      html  css  js  c++  java
  • 深入理解java虚拟机(7)---线程安全 & 锁优化

    关于线程安全的话题,足可以使用一本书来讲解这些东西。<Java Concurrency in Practice> 就是讲解这些的,在这里

    主要还是分析JVM中关于线程安全这块的内容。

    1.线程安全是什么?

    线程安全,有经验的开发人员都听过这个名词,但是能否给到一个准确的定义,很难。

    在 Java Concurrency in Practice里面定义是:

    当多个线程访问一个对象时,如果不用考虑这些线程在运行时的环境下的调度和交替执行,

    也不需要进行额外的同步,或者调用其他协作,这个情况下,线程就是安全的。

    java中的线程安全可以定义5个级别:

    1)不可改变。

    也就是final修饰的词。

    public class ThreadSafeType {
    
    
        class ThreadContext{
            public  int id = -1;
        }
    
        public void doSomeThingBackground(ThreadContext context)
        {
            new Thread(){
                @Override
                public void run(){
                    context.id++;
                }
            }.start();
        }
    }

    上面标红的,会再android studio里面提示错误:

    从内部类中访问本地变量context; 需要被声明为最终类型。

    也就是说在

    public void doSomeThingBackground(final ThreadContext context)

    要改成上面的类型,这就是线程安全的考虑。

    2)绝对安全

    绝对安全其实很难描述,比如Vector是安全的。但是在多线程的情况下,它也是不安全的。

    3)相对安全

    相对安全其实就是我们一般意义上的线程安全。

    它需要保证对这个对象的单独操作是安全的。但是对于特定的顺序,需要一些方法保证线程安全。

    4)线程兼容

    这就是我们常见的情况,需要使用synchronized等手段来保证线程安全。

    5)线程对立

    比较极端的情况,就是无论怎么加锁,代码无法并发运行。一种情况就是死锁。

    2.如何实现线程安全

    1)互斥同步

    保持共享数据在同一时刻只被一个线程使用。

    互斥是手段,同步是目的。

    在java中最常见的就是synchronized方法。

    synchronized标记的代码,会生成monitorenter & monitorexit  2段代码。

    这是java编译器自动生成的,不会有遗漏。使用其他锁,lock & unlock成对出现,但是

    开发者有时候会容易疏忽这个操作,尤其在catch代码里面忘记调用unlock,将是一个隐患。

    java.util.concurrent 下面有不少同步的方法。ReentrantLock也是一个可以的方法,在1.5以前,性能

    远由于synchronized。但是在1.6, java还是把synchronized做了很大的提升。原因就是synchronized使用的

    代码已经远远大于ReentrantLock,并且引入ReentrantLock,可能会令需要开发者混淆。所以ReentrantLock可以认为是

    一道开胃小菜而已。

    2)非阻塞同步

    互斥同步是一种阻塞同步,但是有些情况下,我们不需要互斥,只要能够同步就可以。

    java.util.concurrent.atomic.AtomicInteger

    就是这样一个自增的方法。

        /**
         * Atomically increments by one the current value.
         *
         * @return the previous value
         */
        public final int getAndIncrement() {
            for (;;) {
                int current = get();
                int next = current + 1;
                if (compareAndSet(current, next))
                    return current;
            }
        }

    源码里面没有 互斥的操作,就是一直在循环,知道+1满足就退出。

    者就是非阻塞同步。

    3)无同步

    同步只是保证共享数据的手段,如果2个线程没有共享数据,也就不需要同步。

    3.锁优化

    1)自旋锁

    自旋锁有时候会白白的耗用处理器的资源,但是没有任何实际效果。

    2)锁消除

    如果代码不可能存在共享数据需要同步,编译器就会把锁拿掉

    3)锁粗化

    原则上锁的互斥模块尽可能的小,但是如果对于同一对象,反复的lock & unlock 尤其是循环体中。

    会带来很大的性能损失。

    参考:

    《深入理解Java虚拟机》周志明

    《Java Concurrency in Pratice》

  • 相关阅读:
    web工程导入新环境的注意事项
    Mysql group by,order by,dinstict优化
    Dijkstra and Floyd算法
    百度面试题
    腾讯面试题
    百度笔试3
    百度笔试2
    百度笔试1
    百度2011实习生招聘笔试题
    百度2011.10.16校园招聘会笔试题
  • 原文地址:https://www.cnblogs.com/deman/p/5534079.html
Copyright © 2011-2022 走看看