synchronized

  1、synchronized的几种基本用法:

  https://gitee.com/play-happy/base-project/tree/developer/src/main/java/org/burning/sport/javase/thread/synchronizeds

public class SynchronizedTest1 {

    private Object lock = new Object();
    /**
     * ①对于普通统发方法,锁是当前实例对象
     */
    public synchronized void f() {
    }
    /**
     * ②对于静态同步方法,锁是当前类的Class对象
     */
    public synchronized static void g() {
    }
    /**
     * ③对于普通方法快,锁是synchronized括号里配置的对象
     */
    public void p() {
        synchronized (lock) {
        }
    }
    public void q() {
        synchronized (this) {
        }
    }
public void k() {
  synchronized(SynchronizedTest1.class) {
}
} }

  ps: synchronized方法(比如①)和synchronized代码块(比如③)的性能相比较,①比②性能要低,所以尽量用synchronized代码块

  2、加锁的错误用法

/**
 * 加锁的错误示例
 * 示例中两个加锁的方法,它们的锁对象不是同一个,对于方法f()的锁对象是this,也就是SynchronizedTest2
 * 对于方法g()的锁对象是Object,所以这两个方法的同步是互相独立的
 */
public class SynchronizedTest2 {

    private Object lock = new Object();

    public synchronized void f() {
        for(int i = 0; i < 5; i++) {
            System.out.println("f(" + i + ")");
            Thread.yield();
        }
    }

    public void g() {
        synchronized (lock) {
            for(int i = 0; i < 5; i++) {
                System.out.println("g(" + i + ")");
                Thread.yield();
            }
        }
    }

    public static void main(String[] args) {
        final SynchronizedTest2 test2 = new SynchronizedTest2();
        new Thread() {
            @Override
            public void run() {
                test2.f();
            }
        }.start();
        test2.g();
    }
}

   3、锁的升级与对比

  JavaSE6.0 锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态。这几个状态会锁着锁竞争而升级。

锁可以升级而不能降级

  3.1 偏向锁:

    为了让线程获得锁的代价更低而引入了偏向锁。当一个线程访问同步块并获取锁时,会在对象头帧栈中的锁记录里存储偏向的线程ID,以后该线程在进入和退出同步块时不需要CAS操作来加锁和解锁,只需

  要简单的测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。如果测试成功表示线程已经获得了锁。

  3.2 轻量级锁:

    线程在执行同步块之前,JVM会先在当前线程的帧栈中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中,官方成为Displaced Mark Word。然后线程尝试使用CAS将对象头中的Mark

  Word 替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。

   4、synchronized的可重入性(对比重入锁:ReentrantLock)

      什么叫做锁的重入性? 当一个线程再次请求自己持有对象锁的临界资源时,这种情况属于重入锁。在java中synchronized是基于原子性的内部锁机制,是可重入的,因此在

  一个线程调用synchronized方法的

    同时在其方法体内部调用该对象另一个synchronized方法,也就是说一个线程得到一个对象锁后再次请求该对象锁,是允许的,这就是synchronized的可重入性。

  synchronized是隐式的支持重进入。

Demo1:

public class SynchronizedTest3 implements Runnable{
    int a = 0;
    int b = 0;

    public static void main(String[] args) throws InterruptedException {
        SynchronizedTest3 test = new SynchronizedTest3();
        Thread t1 = new Thread(test);
        Thread t2 = new Thread(test);
        //启动两个线程跑
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(test.a);
        System.out.println(test.b);
    }
    //这个的锁对象是当前实例对象
    public synchronized void increase() {
        b++;
    }

    @Override
    public void run() {
        for(int i = 0; i < 1000; i++) {
            //这个的锁对象也是当前实例对象
            synchronized (this) {
                a++;
                increase();
            }

        }
    }
}
/*
  output:
  2000
  2000
 */

 Demo2:

public class Widget {
    public synchronized void doSomething() {
        ......
    }
}

public class LoggingWidget extends Widget {
    public synchronized void doSomething() {
        //业务操作
        super.doSomething();
    }
}

  5、线程的中断与synchroinzed  参考:https://www.cnblogs.com/happyflyingpig/p/9716055.html

   事实上线程的中断操作对于正在等待获取的锁对象的synchronized方法或者代码块并不起作用,也就是对于synchronized来说,如果一个线程在等待锁,那么结果只有两种,要么它获得这把锁继续执行,要么它

   就保存等待,即使调用中断线程的方法,也不会生效。

    6、等待唤醒机制与synchroinzed

    所谓等待唤醒机制本篇主要指的是notify/notifyAll和wait方法,在使用这3个方法时,必须处于synchronized代码块或者synchronized方法中,否则就会抛出IllegalMonitorStateException异常,

    这是因为调用这几个方法前必须拿到当前对象的监视器monitor对象,也就是说notify/notifyAll和wait方法依赖于monitor对象,我们知道monitor 存在于对象头的Mark Word 中(存储monitor引用指针),

    而synchronized关键字可以获取 monitor ,这也就是为什么notify/notifyAll和wait方法必须在synchronized代码块或者synchronized方法调用的原因。

synchronized (obj) {
       obj.wait();
       obj.notify();
       obj.notifyAll();         
 }

   需要特别理解的一点是,与sleep方法不同的是wait方法调用完成后,线程将被暂停,但wait方法将会释放当前持有的监视器锁(monitor),直到有线程

  调用notify/notifyAll方法后方能继续执行,而sleep方法只让线程休眠并不释放锁。同时notify/notifyAll方法调用后,并不会马上释放监视器锁,而是在

  相应的synchronized(){}/synchronized方法执行结束后才自动释放锁。

参考:

  【1】《Think in Java》第四版

       【2】《Java核心技术》卷Ⅰ 

  【3】《Java并发编程的艺术》,方腾飞

  【4】《Java高并发程序设计》,葛一鸣

  【5】《Java并发编程实战》,童云兰

  【6】博客,https://blog.csdn.net/javazejian/article/details/72828483

  【7】博客,http://www.cnblogs.com/skywang12345/p/3479202.html

posted @ 2018-09-30 06:05  寻找风口的猪  阅读(272)  评论(0编辑  收藏  举报