Synchronized的锁的是什么?

分为两种情况,Synchronized可以锁方法与代码块

  1. 锁方法又可分为锁静态方法与实例方法,前者锁的为当前类,后者锁的是当前实例对象
  2. 锁定代码块时要指定加锁对象(也就是Sychronized后面小括号里填入的对象),该对象可为任意类,例如:Object obj,this,XXX.class。

Synchronized关键字的底层原理

同样分为修饰同步语句块和修饰方法两种情况说明:

  1. 修饰同步语句块:Synchronized在底层编译的jvm指令中,会生成monitorenter和monitorexit两个指令,monitorenter指明同步代码块开始的位置,monitorexit指明同步代码块结束的位置。当执行monitorenter指令时,会尝试获得对象的monitor,monitor是java中所有对象都具有的,包含在对象头中。monitor中有一个计数器,当它为0时表示它是空闲的,当获得锁后计数器加一,执行monitorexit后,计数器减一。moniter锁支持重复加锁
  2. 修饰方法时没有monitorenter和moniterexit指令,取而代之的是一个同步表示ACC_SYNCHRONIZED标识,jvm根据这个标识判断该方法为一个同步方法,从而执行同步调用。

编写10个线程,第一个线程从1加到10,第二个线程从11加到20……第十个线程从91加到100, 最后再,10个线程结果相加

第一版为注释掉的东西,我们可以将synchronized的范围进一步缩小。

public class TenThreadAdd {
    public static void main(String[] args) throws InterruptedException{
        for (int i = 0; i < 10; i++) {
           Thread add = new Add(i);
            add.start();
            add.join();
        }
        System.out.println(Add.sumAll);
    }
}
class Add extends Thread{
   public static  Object lock = new Object();
    public static int sumAll = 0;
    private int begin;
    private int sum = 0;
    public Add(){}
    public Add(int begin){
        this.begin = begin;
    }
    //我们可以对synchronized的范围作进一步缩小
//    public synchronized void run(){
//        for (int i = begin*10+1; i <= begin*10+10; i++) {
//            sum += i;
//        }
//        sumAll += sum;
//        System.out.println(Thread.currentThread().getName()+"---->"+sum);
//    }

        public  void run(){
        for (int i = begin*10+1; i <= begin*10+10; i++) {
            sum += i;
        }
        synchronized (lock){
            sumAll += sum;
        }

        System.out.println(Thread.currentThread().getName()+"---->"+sum);
    }

}

运行截图:
image.png
主线程中必须要添加add.join,目的是避免主线程先于几个子线程结束,从而使最终输出结果错误。若不添加,则可能会出现如下图所示错误:
image.png

Q.E.D.