• java的同步实现


    在java编程中,经常需要用到同步,而同步的实现使用最多的就是synchronized关键字了。

    synchronized关键字涉及到“锁”的概念,首先先了解一下相关锁的知识。

    java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。

    java内置锁是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,直到线程B释放这个锁,如果B线程不释放这个锁,那么A线程将永远等待下去。

    java中的锁按级别可分为:对象锁或方法锁,类锁这两种。

    java的对象锁(或方法锁)和类锁在锁的概念上与内置锁基本一致,但对象锁和类锁之间是有很大区别的。

    区别:1、对象锁用于对象实例方法,类锁是用于类的静态方法或者一个类的class对象上。

         2、类的对象可以有多个,所以不同的对象实例可以有多个对象锁,且互不干扰。每个类只有一个class对象,所以每个类只有一个类锁。

    synchronized的用法:synchronized修改方法或者synchronized修饰代码块。

    下面通过具体实例分析两种用法在对象锁和类锁上区别:

    对象锁的synchronized修饰方法和代码块:

    public class TestSynchronizedForObjectLock {
        
        public void test1(){
            synchronized(this){
                int i=5;
                while(i-- > 0){
                    System.out.println(Thread.currentThread().getName() + ":" + i);
                    
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
        
        public synchronized void test2(){
            int i =5;
            while(i-- > 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) {
            TestSynchronizedForObjectLock t = new TestSynchronizedForObjectLock();
            Thread t1 = new Thread(new Runnable(){public void run(){t.test1();}},"test1");
            Thread t2 = new Thread(new Runnable(){public void run(){t.test2();}},"test2");
            
            t1.start();
            t2.start();
        }
    
    }

    其运行结果:

    test1:4
    test1:3
    test1:2
    test1:1
    test1:0
    test2:4
    test2:3
    test2:2
    test2:1
    test2:0

    上述的代码,第一个方法时用了同步代码块的方式进行同步,传入的对象实例是this,表明是当前对象;第二个方法是修饰方法的方式进行同步。因为第一个同步代码块传入的this,所以两个同步代码所需要获得的对象锁都是同一个对象锁,下面main方法时分别开启两个线程,分别调用test1和test2方法,那么两个线程都需要获得该对象锁,另一个线程必须等待。上面也给出了运行的结果可以看到:直到test1线程执行完毕,释放掉锁,test2线程才开始执行。

    如果我们把test2方法的synchronized关键字去掉,执行结果会如何呢?运行结果:

    test1:4
    test2:4
    test2:3
    test1:3
    test2:2
    test1:2
    test2:1
    test1:1
    test2:0
    test1:0

    上面是执行结果,我们可以看到,结果输出是交替着进行输出的,这是因为,某个线程得到了对象锁,但是另一个线程还是可以访问没有进行同步的方法或者代码。进行了同步的方法(加锁方法)和没有进行同步的方法(普通方法)是互不影响的,一个线程进入了同步方法,得到了对象锁,其他线程还是可以访问那些没有同步的方法(普通方法)。

    所以synchronized只是一个内置锁的加锁机制,当某个方法加上synchronized关键字后,就表明要获得该内置锁才能执行,并不能阻止其他线程访问不需要获得该内置锁的方法。

    类锁的synchronized修饰方法和代码块:

    public class TestSynchronizedForClassLock {
        
        public void test3(){
            
            synchronized(this.getClass()){
                int i=5;
                while(i-- > 0){
                    System.out.println(Thread.currentThread().getName() + ":" + i);
                    
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
        
        public static synchronized void test4(){
            int i=5;
            while(i-- > 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
                
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) {
            TestSynchronizedForClassLock c = new TestSynchronizedForClassLock();
            Thread t3 = new Thread(new Runnable(){public void run(){c.test3();}});
            Thread t4 = new Thread(new Runnable(){public void run(){TestSynchronizedForClassLock.test4();}});
            
            t3.start();
            t4.start();
    
        }
    }

    运行结果:

    Thread-0:4
    Thread-0:3
    Thread-0:2
    Thread-0:1
    Thread-0:0
    Thread-1:4
    Thread-1:3
    Thread-1:2
    Thread-1:1
    Thread-1:0

    其实,类锁修饰方法和代码块的效果和对象锁是一样的,因为类锁只是一个抽象出来的概念,只是为了区别静态方法的特点,因为静态方法是所有对象实例共用的,所以对应着synchronized修饰的静态方法的锁也是唯一的,所以抽象出来个类锁。其实这里的重点在下面这块代码,synchronized同时修饰静态和非静态方法

    public class TestSynchronizedForClassLock {
        
        public synchronized void test3(){
            
                int i=5;
                while(i-- > 0){
                    System.out.println(Thread.currentThread().getName() + ":" + i);
                    
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            
        }
        
        public static synchronized void test4(){
            int i=5;
            while(i-- > 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
                
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) {
            TestSynchronizedForClassLock c = new TestSynchronizedForClassLock();
            Thread t3 = new Thread(new Runnable(){public void run(){c.test3();}});
            Thread t4 = new Thread(new Runnable(){public void run(){TestSynchronizedForClassLock.test4();}});
            
            t3.start();
            t4.start();
    
        }
    }

    运行结果:

    Thread-0:4
    Thread-1:4
    Thread-0:3
    Thread-1:3
    Thread-0:2
    Thread-1:2
    Thread-0:1
    Thread-1:1
    Thread-1:0
    Thread-0:0

    上面代码synchronized同时修饰静态方法和实例方法,但是运行结果是交替进行的,这证明了类锁和对象锁是两个不一样的锁,控制着不同的区域,它们是互不干扰的。同样,线程获得对象锁的同时,也可以获得该类锁,即同时获得两个锁,这是允许的。

  • 相关阅读:
    打印二叉树和为某一值的路径
    顺时针打印数组
    算术表达式
    堆内存与栈内存详解
    【腾讯校招在线考试附加题】将一个10进制数转换为四位定长的36进制数
    反转链表
    记录github出错及解决方案
    centos7操作防火墙
    无法在web.xml或使用此应用程序部署的jar文件中解析绝对uri:[http://java.sun.com/jsp/jstl/core]解决方法
    MyBatis联表查询——别名方式
  • 原文地址:https://www.cnblogs.com/conswin/p/6753573.html
Copyright © 2020-2023  润新知