您的位置:首页 > 其它

五、 锁应用

2016-07-14 00:39 246 查看

5.1、线程安全

多线程访问ArrayList。
代码:
publicstatic List<Integer> numberList =new ArrayList<Integer>();
publicstatic class AddToList implements Runnable{
         int startnum=0;
         public AddToList(int startnumber){
                   startnum=startnumber;
         }
         @Override
         public void run() {
                   int count=0;
                   while(count<1000000){
                            numberList.add(startnum);
                            startnum+=2;
                            count++;
                   }
         }
}
publicstatic void main(String[] args) throws InterruptedException {
         Thread t1=new Thread(new AddToList(0));
         Thread t2=new Thread(new AddToList(1));
         t1.start();
         t2.start();
         while(t1.isAlive() || t2.isAlive()){
                   Thread.sleep(1);
         }
         System.out.println(numberList.size());
}

 

5.2、偏向锁

n  大部分情况是没有竞争的,所以可以通过偏向来提高性能
n  所谓的偏向,就是偏心,即锁会偏向于当前已经占有锁的线程
n  将对象头Mark的标记设置为偏向,并将线程ID写入对象头Mark
n  只要没有竞争,获得偏向锁的线程,在将来进入同步块,不需要做同步
n  当其他线程请求相同的锁时,偏向模式结束
n  -XX:+UseBiasedLocking
–     默认启用
n  在竞争激烈的场合,偏向锁会增加系统负担
代码:
publicstatic List<Integer> numberList =new Vector<Integer>();
publicstatic void main(String[] args) throws InterruptedException {
         long begin=System.currentTimeMillis();
         int count=0;
         int startnum=0;
         while(count<10000000){
                   numberList.add(startnum);
                  startnum+=2;
                   count++;
         }
         long end=System.currentTimeMillis();
         System.out.println(end-begin);
}
-XX:+UseBiasedLocking-XX:BiasedLockingStartupDelay=0
-XX:-UseBiasedLocking
 

5.3、轻量级锁

–      嵌入在线程栈中的对象

普通的锁处理性能不够理想,轻量级锁是一种快速的锁定方法。

如果对象没有被锁定:

1)  将对象头的Mark指针保存到锁对象中

2)  将对象头设置为指向锁的指针(在线程栈空间中)

如果轻量级锁失败,表示存在竞争,升级为重量级锁(常规锁)

在没有锁竞争的前提下,减少传统锁使用OS互斥量产生的性能损耗

在竞争激烈时,轻量级锁会多做很多额外操作,导致性能下降

 

5.4、自旋锁

n  当竞争存在时,如果线程可以很快获得锁,那么可以不在OS层挂起线程,让线程做几个空操作(自旋)

n  JDK1.6中-XX:+UseSpinning开启

n  JDK1.7中,去掉此参数,改为内置实现

n  如果同步块很长,自旋失败,会降低系统性能

n  如果同步块很短,自旋成功,节省线程挂起切换时间,提升系统性能

 

 

内置于JVM中的获取锁的优化方法和获取锁的步骤

–      偏向锁可用会先尝试偏向锁

–      轻量级锁可用会先尝试轻量级锁

–      以上都失败,尝试自旋锁

–      再失败,尝试普通锁,使用OS互斥量在操作系统层挂起

 

 

5.5、synchronized

1)减少锁持有时间

publicsynchronized void syncMethod(){

   othercode1();

   mutextMethod();

   othercode2();

}

将synchronized置于代码块中

public voidsyncMethod2(){

   othercode1();

   synchronized(this){

            mutextMethod();

   }

   othercode2();

}

 

2)减小锁的粒度

将大对象,拆成小对象,大大增加并行度,降低锁竞争

偏向锁,轻量级锁成功率提高

HashMap的同步实现

public Vget(Object key) {

            synchronized (mutex) {returnm.get(key);}

        }

public V put(Kkey, V value) {

            synchronized (mutex) {returnm.put(key, value);}

}

 

5.6、锁分离

n  根据功能进行锁分离

n  ReadWriteLock

n  读多写少的情况,可以提高性能

 

 

读锁

写锁

读锁

可访问

不可访问

写锁

不可访问

不可访问

 

n  LinkedBlockingQueue

 

5.7、锁粗化

通常情况下,为了保证多线程间的有效并发,会要求每个线程持有锁的时间尽量短,即在使用完公共资源后,应该立即释放锁。只有这样,等待在这个锁上的其他线程才能尽早的获得资源执行任务。但是,凡事都有一个度,如果对同一个锁不停的进行请求、同步和释放,其本身也会消耗系统宝贵的资源,反而不利于性能的优化

 

 

for(inti=0;i<CIRCLE;i++){

   synchronized(lock){

             

   }

}

 

synchronized(lock){

for(inti=0;i<CIRCLE;i++){

             

   }

}

5.8、消除锁

在即时编译器时,如果发现不可能被共享的对象,则可以消除这些对象的锁操作

public static void main(String args[])throws InterruptedException {

         longstart = System.currentTimeMillis();

         for(int i = 0; i < CIRCLE; i++) {

                   craeteStringBuffer("JVM","Diagnosis");

         }

         longbufferCost = System.currentTimeMillis() - start;

         System.out.println("craeteStringBuffer:" + bufferCost + " ms");

}

public static StringcraeteStringBuffer(String s1, String s2) {

         StringBuffersb = new StringBuffer();

         sb.append(s1);

         sb.append(s2);

         returnsb.toString();

}

设置CIRCLE= 2000000

-server -XX:+DoEscapeAnalysis-XX:+EliminateLocks

createStringBuffer: 187 ms

 

-server -XX:+DoEscapeAnalysis-XX:-EliminateLocks

createStringBuffer: 254 ms

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: