Java 并发总结, synchronized详解

Java 并发总结, synchronized详解

  • Java 并发总结, synchronized详解
    • 1 线程安全
    • 2 互斥同步
    • 3 synchronized特性
    • 4 synchronized的三种应用方式
      • 4.1 synchronized修饰实例方法
      • 4.2 synchronized作用于静态方法
      • 4.3 synchronized作用于代码块
    • 5 synchronized底层实现原理(待补充)
    • 6 Java三大变量的线程安全(待补充)

Java 并发总结, synchronized详解

1 线程安全

​ 当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果。那么这个对象就是线程安全的。

​ Java 语言中得各种操作共享数据可以分成五类,按安全程度由强到弱来排序:

1 不可变:不可变的对象一定是线程安全的,无论对象的方法实现还是方法的调用都不需要在采取措施。

​ 如果共享数据是一个基本数据类型,那么只要在定义的时候使用final 关键字修饰就可以保证它不可变;如果共享数据是一个对象,需要对象自行保证其行为不会对其状态产生任何影响。

2 绝对线程安全:绝对线程安全是不管运行环境如何,调用者都不需要任何额外的同步措施。通常需要付出很大的甚至不切实际的代价。

3 相对线程安全:就是通常意义的线程安全,确保这个对象单独的操作是安全的。Java语言中,大部分声称线程安全的类都属于这种类型,如Vector,Hashtable。

4 线程兼容:指的是本身对象并不是线程安全的,但是可以通过调用端的正确使用同步手段来保证对象的安全使用。通常我们说一个类不是线程安全的,指的就是这种状态,如:ArrayList,HashMap等。

5 线程对立:无论调用端是否采取了同步措施,都无法在多线程环境中并发使用。

2 互斥同步

互斥和同步是一种最常见、最主要的实现并发正确性(相对线程安全)的保障手段。

同步:指的是多个线程并发访问共享数据时,保证共享数据在同一个时刻只能被一个线程使用。

互斥:指的是实现同步的一种手段,临界区互斥量和信号量都是主要的互斥实现方式。

在java中,最基本的互斥同步手段就是synchronized关键字。synchronized可以保证线程竞争共享资源的正确性(多个线程并发访问共享数据时,保证共享数据在同一个时刻只能被一个线程使用)。

3 synchronized特性

原子性:确保线程互斥的访问同步代码。synchronized保证只有一个线程拿到锁,进入同步代码块操作共享资源,因此具有原子性。

可见性:保证共享变量的修改能够及时课件。执行synchronized时,会对应执行lock,unlock原子操作。lock操作,就会清空工作空间该变量的值;执行unlock操作之前,必须先把变量同步回主内存中。

有序性:synchronized内的代码和外部的代码禁止排序,至于内部的代码,则不会禁止排序,但是由于只有一个线程进入同步代码块,因此在同步代码块中相当于是单线程的,根据as-if-serial语义,即使代码块内部发生了重排序,也不会影响程序执行的结果。

悲观锁:synchronized是悲观锁,每次使用共享资源时都认为会和其他线程产生竞争,所以每次使用共享资源都会上锁。

独占锁:synchronized是独占锁,该锁一次只能被一个线程所持有,其他线程被阻塞。

非公平锁:synchronized是非公平锁,线程获取锁的顺序可以不按照线程的阻塞顺序,允许线程发出请求后立即尝试获取锁。

可重入锁:synchronized是可重入锁。持锁线程可以再次获取自己的内部的锁。

Tips:

1 悲观锁 or 乐观锁:是否一定要锁。

2 共享锁 or 独占锁(排他锁):是否可以有多个线程同时拿锁。

3 公平锁 or 非公平锁:是否按阻塞顺序拿锁。

4 可重入锁 or 不可重入锁: 拿锁线程是否可以多次拿锁。

4 synchronized的三种应用方式

4.1 synchronized修饰实例方法

作用于给当前实例加锁,进入同步代码前要获得当前实例的锁。(注意:是当前实例,一个类有N个实例,一个实例有一把锁)。

使用synchronized 修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着,且共享对象一定是this,并且同步代码块是整个方法体。

示例1

public class HasSelfPrivateNum {
   
    private int num = 0;
    synchronized public void addI(String username){
   
        try {
   
            if ("a".equals(username)){
   
                num = 100;
                System.out.println("a set over!   " + System.currentTimeMillis());
                Thread.sleep(2000);
            }else {
   
                num = 200;
                System.out.println("b set over!   " + System.currentTimeMillis());
            }
        }catch (InterruptedException e){
   
            e.printStackTrace();
        }
    }
}
public class ThreadA extends Thread{
   
    private HasSelfPrivateNum numRef;

    public ThreadA(HasSelfPrivateNum numRef) {
   
        this.numRef = numRef;
    }

    @Override
    public void run() {
   
        super.run();
        numRef.addI("a");
    }
}
public class ThreadB extends Thread{
   
    private HasSelfPrivateNum numRef;

    public ThreadB(HasSelfPrivateNum numRef) {
   
        this.numRef = numRef;
    }

    @Override
    public void run() {
   
        super.run();
        numRef.addI("b");
    }
}
public class TestRun {
   
    public static void main(String[] args) {
   
        HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
        HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();
        ThreadA athread = new ThreadA(numRef1);
        athread.start();
        ThreadB bthread = new ThreadB(numRef2);
        bthread.start();
    }
}

运行结果如下:
Java 并发总结, synchronized详解_第1张图片

synchronized numRef1 和 numRef2 是两个不同的类,同步方法的锁对象不是同一个对象,无法达到同步的目的,解决方法是让synchronized 锁对象是同一个,示例代码如下,运行结果如下图。

public class TestRun {
   
    public static void main(String[] args) {
   
        HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
        //HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();
        ThreadA athread = new ThreadA(numRef1);
        athread.start();
        ThreadB bthread = new ThreadB(numRef1);
        bthread.start();
    }
}

运行结果如图:

Java 并发总结, synchronized详解_第2张图片

4.2 synchronized作用于静态方法

表示找类锁,类锁永远只有一把,就算创建了100个对象,那类锁也只有一把。

对象锁:一个对象一把锁,

类锁:100个对象,也是一把锁。

当synchronized 作用于静态方法时,其锁就是当前类的class对象锁。由于静态成员不专属于任何一个实例对象,是类成员,因此通过class对象锁可以控制静态成员的并发操作。需要注意的是如果一个线程A调用一个实例对象的非static sync

你可能感兴趣的:(java,java)