您好,登录后才能下订单哦!
本篇文章为大家展示了synchronized的特性有哪些,代码简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。
1. synchronized锁重入
1.1 介绍
关键字synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的。这说明在一个synchronized方法/块内部调用本类的其他synchronized方法/块时,是永远可以得到锁的。 
例如:
public class Service1 {
    public synchronized void method1(){
        System.out.println("method1");
        method2();
    }
    public synchronized void method2(){
        System.out.println("method2");
        method3();
    }
    public synchronized void method3(){
        System.out.println("method3");
    }
}public class MyThread extends Thread {
    @Override
    public void run(){
        Service1 service1 = new Service1();
        service1.method1();
    }
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
}运行结果如下: 
☹ 看到这个结果的时候是茫然了,怎么就证明是可重入锁的了呢? 
➤ “可重入锁”的概念是:自己可以再次获取自己的内部锁,比如有1个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁。 
➤ “可重入锁”的最大作用就是避免死锁
1.2 分析
我们知道,在程序中,是无法显式释放对同步监视器的锁的,而会在如下几个情况下释放锁: 
① 当前线程的同步方法、代码块执行结束的时候释放 
② 当前线程在同步方法、同步代码块遇到break、return终止该代码块或方法的时候释放 
③ 出现未处理的error或exception导致异常结束的时候释放 
④ 程序执行了同步对象wait方法,当前线程暂停,释放锁
那么,在上面的程序中,当线程进入同步方法method1时获得Service1的对象锁,但是在执行method1的时候调用了同步方法method2,按照正常情况,在执行同步方法method2同样需要获得对象锁,但是根据上面释放锁的条件,此时method1的对象锁还没有释放,此时就会造成死锁,无法继续执行method2。但是通过上面代码的执行结果来看,能够正常执行method2和method3,这就说明,在一个Synchronized修饰的方法或代码块的内部调用本类的其他Synchronized修饰的方法或代码块时,是永远可以得到锁的。
1.3 父子可继承性
可重入锁支持在父子类继承的环境中,示例代码如下:
public class Service2 {
    public int i = 10;
    public synchronized void mainMethod(){
        i--;
        System.out.println("main print i="+i);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}public class Service3 extends Service2 {
    public synchronized void subMethod(){
        try{
            while (i>0){
                i--;
                System.out.println("sub print i= "+i);
                Thread.sleep(100);
                this.mainMethod();
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}public class MyThread extends Thread {
    @Override
    public void run(){
        Service3 service3 = new Service3();
        service3.subMethod();
    }
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
}运行结果如下: 
此程序说明,当存在父子类继承关系时,子类完全可以通过“可重入锁”调用父类的同步方法。
2. 出现异常,锁自动释放
当一个线程执行的代码出现异常时,其所持有的锁会自动释放。 
验证代码如下:
public class Service4 {
    public synchronized void testMethod(){
        if(Thread.currentThread().getName().equals("a")){
            System.out.println("ThreadName= "+Thread.currentThread().getName()+" run beginTime="+System.currentTimeMillis());
            int i=1;
            while (i == 1){
                if((""+Math.random()).substring(0,8).equals("0.123456")){
                    System.out.println("ThreadName= "+Thread.currentThread().getName()+" run exceptionTime="+System.currentTimeMillis());
                  //Integer.parseInt("a");
                }
            }
        }else{
            System.out.println("Thread B run time= "+System.currentTimeMillis());
        }
    }
}public class ThreadA extends Thread{
    private Service4 service4;
    public ThreadA(Service4 service4){
        this.service4 = service4;
    }
    @Override
    public void run(){
        service4.testMethod();
    }
}public class ThreadB extends Thread{
    private Service4 service4;
    public ThreadB(Service4 service4){
        this.service4 = service4;
    }
    @Override
    public void run(){
        service4.testMethod();
    }
}public class Main {
    public static void main(String[] args) {
        try {
            Service4 service4 = new Service4();
            ThreadA a = new ThreadA(service4);
            a.setName("a");
            a.start();
            Thread.sleep(500);
            ThreadB b = new ThreadB(service4);
            b.setName("b");
            b.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}注意Service4类中Integer.parseInt(“a”);此时处于被注释状态,运行结果如下: 
由于a线程没有错误,while(true),此时a线程处于无限循环状态,锁一直被a占用,b线程无法获得锁,即无法执行b线程。
将Service4类中Integer.parseInt(“a”);解开注释,执行的结果如下:

   
当a线程发生错误时,b线程获得锁从而执行,由此可见,当方法出现异常时,锁自动释放。
3. 将任意对象作为监视器
java支持对“任意对象”作为“对象监视器”来实现同步的功能。这个“任意对象”大多数是实例变量及方法的参数,使用格式为synchronized(非this对象x)同步代码块。 
示例代码如下:
public class StringLock {
    private String lock = "lock";
    public void method(){
        synchronized (lock){
            try {
                System.out.println("当前线程: "+Thread.currentThread().getName() + "开始");
                Thread.sleep(1000);
                System.out.println("当前线程: "+Thread.currentThread().getName() + "结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        final StringLock stringLock = new StringLock();
        new Thread(new Runnable() {
            @Override
            public void run() {
                stringLock.method();
            }
        },"t1").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                stringLock.method();
            }
        },"t2").start();
    }
}运行结果如下: 
锁非this对象具有一定的优点:如果在一个类中有很多个synchronized方法,这时虽然能实现同步,但会受到阻塞,所以影响运行效率;但如果使用同步代码块锁非this对象,则synchronized(非this)代码块中的程序与同步方法是异步的,不予其他锁this同步方法争抢this锁,则可大大提高运行效率。
4. 同步不具有继承性
父类的同步方法,在子类中重写后不加同步关键字,是不会同步的,所以还得在子类的方法中添加synchronized关键字。
上述内容就是synchronized的特性有哪些,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注亿速云行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。