多线程(二)Synchronized

  synchronized是Java中的关键字,是一种同步锁,按类型可以分为同步方法和同步代码块。

 

 非同步实例方法&同步实例方法:

非同步实例方法:

public class TestSync {
    public static void main(String[] args) throws InterruptedException {
        Cls c = new Cls();
        new Thread() {
            @Override
            public void run() {
                try {
                    c.method();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }.start();
        
        new Thread() {
            @Override
            public void run() {
                try {
                    c.method();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }.start();
    }
}

class Cls {

    public void method() throws InterruptedException {
        Thread.sleep(10);
        System.out.println(Thread.currentThread().getName() + " menthod begin..");
        Thread.sleep(10);
        System.out.println(Thread.currentThread().getName() + " menthod end..");
    }


}
View Code

执行结果(场景:同步方法属于同一个实例对象):

Thread-0 menthod begin..
Thread-1 menthod begin..
Thread-1 menthod end..
Thread-0 menthod end..

同步实例方法:

public class TestSync {
    public static void main(String[] args) throws InterruptedException {
        Cls c = new Cls();
        new Thread() {
            @Override
            public void run() {
                try {
                    c.method();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }.start();
        
        new Thread() {
            @Override
            public void run() {
                try {
                    c.method();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }.start();
    }
}

class Cls {

    public synchronized void method() throws InterruptedException {
        Thread.sleep(10);
        System.out.println(Thread.currentThread().getName() + " menthod1 begin..");
        Thread.sleep(10);
        System.out.println(Thread.currentThread().getName() + " menthod1 end..");
    }

}
View Code

执行结果:

Thread-0 menthod1 begin..
Thread-0 menthod1 end..
Thread-1 menthod1 begin..
Thread-1 menthod1 end..

同步实例方法&同步静态方法:

同步实例方法(场景:同步方法属于不同的实例对象):

public class TestSync {
    public static void main(String[] args) throws InterruptedException {
        Cls c = new Cls();
        new Thread() {
            @Override
            public void run() {
                try {
                    c.method();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }.start();
        Cls c1 = new Cls();
        new Thread() {
            @Override
            public void run() {
                try {
                    c1.method();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }.start();
    }
}

class Cls {


    public static synchronized void staticMethod() throws InterruptedException {
        Thread.sleep(10);
        System.out.println(Thread.currentThread().getName() + " staticMenthod begin..");
        Thread.sleep(10);
        System.out.println(Thread.currentThread().getName() + " staticMenthod end..");
    }
    
    
    public synchronized void method() throws InterruptedException {
        Thread.sleep(10);
        System.out.println(Thread.currentThread().getName() + " menthod begin..");
        Thread.sleep(10);
        System.out.println(Thread.currentThread().getName() + " menthod end..");
    }

}
View Code
Thread-1 menthod begin..
Thread-0 menthod begin..
Thread-0 menthod end..
Thread-1 menthod end..

同步静态方法:

public class TestSync {
    public static void main(String[] args) throws InterruptedException {
        new Thread() {
            @Override
            public void run() {
                try {
                    Cls.staticMethod();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }.start();
        new Thread() {
            @Override
            public void run() {
                try {
                    Cls.staticMethod();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }.start();
    }
}

class Cls {


    public static synchronized void staticMethod() throws InterruptedException {
        Thread.sleep(10);
        System.out.println(Thread.currentThread().getName() + " staticMenthod begin..");
        Thread.sleep(10);
        System.out.println(Thread.currentThread().getName() + " staticMenthod end..");
    }
    
    
    public synchronized void method() throws InterruptedException {
        Thread.sleep(10);
        System.out.println(Thread.currentThread().getName() + " menthod begin..");
        Thread.sleep(10);
        System.out.println(Thread.currentThread().getName() + " menthod end..");
    }

}
View Code

执行结果:

Thread-0 staticMenthod begin..
Thread-0 staticMenthod end..
Thread-1 staticMenthod begin..
Thread-1 staticMenthod end..

结论:

  • 被锁对象是类的实例对象时,synchronized只在同一实例中作用
  • 被锁对象是类对象时,即使new多个实例对象,但他们仍然是属于同一个类,依然会被锁住,即线程之间保证同步关系。

 

锁机制 

public class TestSync {
    
    public  void lockClass() {
        synchronized(TestSync.class) {
            System.out.println("lockClass ..");
        }
    }
    
    public synchronized void lockObject() {
        System.out.println("lockObject ..");
    }
}

  使用【javap -v TestSync.class】来反编译以上代码,结果如下:

  public void lockClass();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=1
         0: ldc           #1                  // class com/ryj/thread/TestSync
         2: dup
         3: astore_1
         4: monitorenter
         5: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;
         8: ldc           #21                 // String lockClass ..
        10: invokevirtual #23                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        13: aload_1
        14: monitorexit
        15: goto          21
        18: aload_1
        19: monitorexit
        20: athrow
        21: return

  public synchronized void lockObject();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #35                 // String lockObject ..
         5: invokevirtual #23                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return

  对于同步方法,JVM采用ACC_SYNCHRONIZED标记符来实现同步。 对于同步代码块。JVM采用monitorentermonitorexit两个指令来实现同步。

  方法级的同步是隐式的。同步方法的常量池中会有一个ACC_SYNCHRONIZED标志。当某个线程要访问某个方法的时候,会检查是否有ACC_SYNCHRONIZED,如果有设置,则需要先获得监视器锁,然后开始执行方法,方法执行之后再释放监视器锁。这时如果其他线程来请求执行方法,会因为无法获得监视器锁而被阻断住。值得注意的是,如果在方法执行过程中,发生了异常,并且方法内部并没有处理该异常,那么在异常被抛到方法外面之前监视器锁会被自动释放。

  执行monitorenter指令理解为加锁,执行monitorexit理解为释放锁。 每个对象维护着一个记录着被锁次数的计数器。未被锁定的对象的该计数器为0,当一个线程获得锁(执行monitorenter)后,该计数器自增变为 1 ,当同一个线程再次获得该对象的锁的时候,计数器再次自增。当同一个线程释放锁(执行monitorexit指令)的时候,计数器再自减。当计数器为0的时候。锁将被释放,其他线程便可以获得锁。

  Java的线程是映射到操作系统原生线程之上的,如果要阻塞或唤醒一个线程就需要操作系统的帮忙,这就要从用户态转换到核心态,因此状态转换需要花费很多的处理器时间,对于代码简单的同步块(如被synchronized修饰的get 或set方法)状态转换消耗的时间有可能比用户代码执行的时间还要长,所以说synchronized是java语言中一个重量级的操纵。

 

posted @ 2021-02-12 14:21  鄙人取个名字好难  阅读(53)  评论(0编辑  收藏  举报