多线程 - (二)synchronized

线程安全类

当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要额外同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的

线程安全包含的特性

原子性:提供了互斥访问,同一时刻只能有一个线程对它进行操作

可见性:一个线程对主内存的修改可以及时被其他线程观察到

有序性:一个线程观察其他线程的指令执行顺序,由于指令重排序的存在,该观察结果一般杂乱无序。

原子性

保证原子性的手段通常有以下几种

这里写图片描述

当需要同步时,可能我们首先想到的就是synchronized,但是在竞争特别激烈时,synchronized可能并不是很好的选择,synchronized的使用方式大概有以下几种

  • 修饰静态方法
  • 修饰普通方法
  • 修饰代码块

修饰静态方法

此时的锁是当前类的字节码,类名.class

    public static synchronized void task() {
        for (int i = 0; i < 5; i++) {
            System.out.println("当前线程名:" + Thread.currentThread().getName() + ",循环的次数:" + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

修饰普通方法

此时的锁是当前对象,this

    public synchronized void task() {
        for (int i = 0; i < 5; i++) {
            System.out.println("当前线程名:" + Thread.currentThread().getName() + ",循环的次数:" + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

修饰代码块

此时的锁是任意对象

    public void task() {

        // this锁
        synchronized (this) {
            for (int i = 0; i < 5; i++) {
                System.out.println("当前线程名:" + Thread.currentThread().getName() + ",循环的次数:" + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        // 任意对象
        Object obj = new Object();
        synchronized (obj){

        }

        // 字节码
        synchronized (this.getClass()){

        }

        // 字节码
        synchronized (RunnableImpl.class){

        }

    }

注意

锁内部代码发生异常,锁自动释放

synchronized会保证共享变量的可见性(保证同步)
java内存模型中关于synchronized有两条规定
1、线程解锁前,必须把共享变量的最新值刷新到主内存
2、线程加锁时将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(加锁与解锁必须是同一把锁)

不要使用String的常量加锁,会出现死循环问题

        // 不要这么使用
        synchronized ("字符串常量"){

        }

synchronized拥有锁重入功能
也就是在使用synchronized时,当一个线程得到一个对象的锁之后,再次请求此对象时是可以再次得到该对象的锁的

    public synchronized void methodA(){
        methodB();
    }

    public synchronized void methodB(){
        methodC();
    }

    public synchronized void methodC(){

    }

猜你喜欢

转载自blog.csdn.net/qq_28988969/article/details/80590984