zoukankan      html  css  js  c++  java
  • synchronized使用说明

    好久没有更新博客了,今天试着用简单的语言把synchronized的使用说清楚。

    synchronized是什么?

    synchronized是用来保证在多线程环境下代码同步执行的可重入的互斥锁。所谓互斥锁是指锁一旦被某个线程拿到之后,其他线程就无法获取锁,只能等到持有锁的线程的释放之后重新尝试获取。至于可重入,则是指已经获取到锁的那个线程在访问加锁代码块的时候是不需要重新获取锁的,可以直接执行代码块,在稍后的例子中也会有这样的测试代码来验证这一点。

    synchronized怎么用?

    synchronized有两种使用方式:
    1、作用于方法声明处;

    public synchronized void test() {
        // code here...
    }
    

    2、作用于代码块。

    public void test() {
        // 1. 锁只针对当前对象
        synchronized(this) {
        	// code here...
        }
        // 2. 针对某一个对象加锁
        synchronized(obj) {
        	// code here...
        }
        // 3. 针对某一个类加锁
        synchronized(XXX.class) {
        	// code here...
        }
    }
    

    如果方法内部的代码全部在synchronized(this){}中的话,作用是与第一种方式等价的,内部实现是否一样,这个就不得而知了。

    对没有声明锁的方法,或者代码块,不会受到锁的限制的。

    死锁

    所谓死锁用最简单的话描述就是,两个线程互相持有对方的需要的锁形成的一种僵持状态。比如线程A持有锁Y,等线程B释放锁X,而线程B持有锁X,等线程A释放锁Y。可以通过jstack检测死锁相关问题。避免死锁的发生,需要线程按照顺序获取需要的锁,避免形成竞争。说来简单,coding的时候还要主动注意才行。

    synchronized 和 ReentrantLock

    两者都是可重入的互斥锁,在语义上可以认为是一样的。区别就是synchronized自动获取锁,执行完毕后自动释放锁,为代码编码带来了较大的便利,同时因为这种便利,也失去灵活性。我们无法控制尝试加锁、主动解锁的场景,ReentrantLock则提供了这种方法的使用,而ReentrantLock的就需要自己来释放锁了,锁一定要放在finally代码块里释放,否则。。。哼哼

    测试代码

    public class TestSynchronized {
    
        public static void main(String[] args) throws InterruptedException {
            byte[] lock = new byte[0];
            final MyRunnable runnable1 = new MyRunnable();
            final MyRunnable runnable2 = new MyRunnable();
            Thread thread1 = new Thread(runnable1);
            thread1.start();
            Thread thread2 = new Thread(runnable2);
            thread2.start();
    
            // 保证上面的线程先start
            Thread.sleep(100L);
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    runnable1.testCall();
                }
            }).start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    runnable2.testCall();
                }
            }).start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    runnable1.testCall2();
                }
            }).start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    runnable2.testCall2();
                }
            }).start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    runnable1.testCall3();
                }
            }).start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    runnable2.testCall3();
                }
            }).start();
    
            Thread thread3 = new Thread(new MyLockRunnable(lock));
            thread3.start();
    
            Thread thread4 = new Thread(new MyLockRunnable(lock));
            thread4.start();
    
            Thread thread7 = new Thread(new MysynchronizedClassRunnable());
            thread7.start();
    
            Thread thread8 = new Thread(new MysynchronizedClassRunnable());
            thread8.start();
    
        }
    
        public static class MyRunnable implements Runnable {
            // 防止测试可重入的时候陷入无限递归
            boolean testIn = false;
            @Override
            public void run() {
                // 锁只针对当前对象
                synchronized (this) {
                    System.out.println(this.getClass().getName());
                    // 可重入测试
                    if (!testIn) {
                        testIn = true;
                        this.run();
                    }
                    while (true) {}
                }
            }
    
            // 因为run方法一直持有当然对象,所以这个方法永远获取不到锁。
            public void testCall() {
                synchronized (this) {
                    System.out.println(Thread.currentThread().getName() + " testCall...");
                }
            }
            // 等价于上面的testCall方法,锁定代码块,只不过这个是锁定的方法级别的代码块
            public synchronized void testCall2() {
                System.out.println(Thread.currentThread().getName() + " testCall2...");
            }
    
            // 非锁定方法,可以正常调用
            public void testCall3() {
                System.out.println(Thread.currentThread().getName() + " testCall3...");
            }
        }
    
        public static class MyLockRunnable implements Runnable {
            private final byte[] lock;
            MyLockRunnable(byte[] lock) {
                this.lock = lock;
            }
    
            @Override
            public void run() {
                // 针对所有引用这个lock对象的方法
                synchronized (lock) {
                    System.out.println(this.getClass().getName());
                    while (true) {}
                }
            }
        }
    
        public static class MysynchronizedClassRunnable implements Runnable {
            @Override
            public void run() {
                // 针对这个这个class加锁
                synchronized (this.getClass()) {
                    System.out.println(this.getClass().getName());
                    while (true) {}
                }
            }
        }
    }
    
  • 相关阅读:
    字符串习题小结
    字符串处理指令以及控制台输入
    初次接触JAVA有关重点
    常用的正则表达式
    JS里日历的两种写法
    win10 系统连不上打印机 操作无法完成(错误Ox00000709) 台式机无线网卡 设置固定IP 之后 IP变了
    win10 visual studio2019 目标框架选不到.net 4.8
    JsonPath 简单入门 与 xpath
    IIS 搭建HTTPS站点
    java mave 打包问题 发布找不到驱动类
  • 原文地址:https://www.cnblogs.com/liushijie/p/5835978.html
Copyright © 2011-2022 走看看