Synchronized理解

 

 我们可以利用synchronized关键字来对程序进行加锁。它既可以用来声明一个synchronized代码块,也可以直接标记静态方法或者实例方法。
 
 
synchronized怎么实现线程的同步?
早期的synchronized属于重量级锁,依赖于mutex lock实现,线程之间的切换涉及到 用户态--》内核态的转换,由于状态的改变设计一个线程的所有变量状态的保存,以及空间的重新分配,系统开销十分巨大。
jdk1.6之后,引入了偏向锁,轻量级锁,在并发程度不是很高的情况下,也能保证良好的性能。
 
 
锁升级的步骤?
无锁--》偏向锁--》轻量级锁--》重量级锁
偏向锁: 目的是减少同一线程获取锁的代价。大多数情况下,锁是不存在多线程竞争的,总是由同一个线程多次获得。
       偏向锁如何加:如果一个线程获得了锁,锁就进入了偏向模式。对象头MarkWorld的锁标志微为01,同时记录了该线程的线程id,当该线程再次请求锁的时候,无需再做任何同步操作,只需检查MarkWorld的锁标记位位现象锁以及线程id相同即可,色和功能区了中间有关锁申请的操作
轻量级锁:有偏向锁升级而来,偏向锁运行的一个线程进入同步块的情况下。当第二个线程加入锁竞争的时候,偏向锁就会升级为轻量级的锁。
  适用场景:线程交替执行同步块。
 
 

Synchronized和ReentrantLock区别?
1.Synchronized基于操作 MarkWord,ReentrantLock 调用Unsafe类的方法
2.ReentrantLock可以对获取锁的等待时间进行设置,避免死锁
3.ReentrantLock可以实现公平,非公平锁
4.ReentrantLock配合condition可以实现更加精确的线程控制
5.Synchronized无需进行锁的释放,ReentrantLock需要进行锁的释放

 
 
 
 

synchronized的线程同步在JVM层面,通过字节码中的monitorenter和monitorexit指令。

    public void test(Object lock){
        synchronized (lock){
            lock.toString();
        }
    }

 

 

如果使用synchronized标记方法,你会看到字节码中方法的访问标记包括ACC_SYNCHRONIZED。
该标记表示在进入该方法时,Java 虚拟机同样需要进行monitorenter操作。

 

 

/**
 * @Author:daboluo
 * @Date: 2019/9/29 8:58
 * @Version 1.0
 *
 * SynchronizedCountExample保证多线程相加原子性
 */
public class SynchronizedCountExample {

    private static int clientTotal = 5000;

    private static int threadTotal = 200;

    private static int count = 0;


    public static void main(String[] args) throws Exception{
        ExecutorService exec = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal; i++) {
            exec.execute(()->{
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        exec.shutdown();
        System.out.println("count = " + count);
    }


    /**
     *
     * synchronized : 不可终端的,适合竞争不激烈的,可读性号
     * lovk:可中断锁,多样化同步,竞争激烈时能维持常态
     * atomic:竞争激烈时能维持常态,比lock的性能号;侄儿能同步一个值
     *
     *
     */
    private synchronized static void add(){
        count++;
    }

}

/**
 * 结果:
 * count = 5000
 * 
 */

  

 

锁优化的方案?

1.降低锁的时间(只需要在有线程安全要求的程序代码上加锁,而不是整个代码块加)

2.降低锁的细粒度(之前的ConcurrentHashMap通过减少锁的细粒度,提升性能)

3.读写分离(应用系统中,读操作次数远远大于写操作)

4.锁的粗化(通过把synchronized加载for循环外,避免每次for循环都涉及线程的切换)

5.锁消除(StringBuffer是一个局部变量,堆栈封闭,方法没有把StringBuffer返回,所以不可能会有多线程去访问它。

当我们在一些不会有线程安全的情况下使用这些类的方法时,达到某些条件时,编译器会将锁消除来提高性能。) 

posted @ 2019-10-04 18:03  Jemb  阅读(263)  评论(0)    收藏  举报