并发访问变量之synchronized与volatile

synchronized

synchronized可以用来保障原子性、可见性和有序性。
原子性:即操作为一个整体,不可再分。
可见性:即数据可见性,由于线程有私有内存可能导致各个线程之间数据的不一致;而synchronized可以解决这个问题,使线程在访问这些数据时从共有内存中进行访问。
有序性:由于JVM会对代码进行指令重排,导致执行结果的错乱;synchronized可以保证代码的整体有序性;例:B代码依赖A的结果,C代码依赖B的结果,那么,A的执行顺序是不需要管理的,C代码的执行顺序是不需要管理的,但是需要整体的执行顺序为A->B->C的执行顺序,synchronized就能够保证代码整体上的执行有序性。

synchronized锁的种类

synchronized的锁分为三种:(当前)对象锁、类对象锁和Object锁。

对象锁——synchronized修饰普通方法、synchronized(this)和synchronized(Object)

最常见的形式就是对象级锁。
通过synchtonized修饰方法、synchronized(this)或synchronized(Object)的方式进行上锁(也就是同步方法与同步代码块),就是对象级的锁;顾名思义,对象级锁锁住的是整个对象,当A线程获取到对象X的对象锁后,B线程无法访问X的任何同步方法;只有A线程释放锁后,B线程才能够进行访问。即:A获取对象锁,其他线程无法访问该对象所有被synchronized修饰的方法(同步方法),但可以异步调用该对象的非synchronized修饰的方法。
同步方法与同步代码块的区别在于:同步方法执行效率比同步代码块低
其中,synchronized(this)是对当前对象作为锁,而synchronized(Object)是以任意对象作为锁。

	public class MyObject {
    
    
	    synchronized public void methodA(){
    
    
	        try {
    
    
	            System.out.println("begin methodA threadName=" + Thread.currentThread().getName());
	            Thread.sleep(5000);
	            System.out.println("end endTime=" + System.currentTimeMillis());
	        }catch (Exception e){
    
    
	            e.printStackTrace();
	        }
	    }
	
	    synchronized public void methodB(){
    
    
	        try {
    
    
	            System.out.println("begin methodB threadName=" + Thread.currentThread().getName() + "begin time=" + System.currentTimeMillis());
	            Thread.sleep(5000);
	            System.out.println("end");
	        }catch (Exception e){
    
    
	            e.printStackTrace();
	        }
	    }
	}
	
	public class ThreadA extends Thread {
    
    
	    private MyObject object;
	
	    public ThreadA(MyObject object) {
    
    
	        this.object = object;
	    }
	
	    @Override
	    public void run() {
    
    
	        object.methodA();
	    }
	}

	public class ThreadB extends Thread {
    
    
	    private MyObject object;
	
	    public ThreadB(MyObject object) {
    
    
	        this.object = object;
	    }
	
	    @Override
	    public void run() {
    
    
	        object.methodB();
	    }
	}

	public class Run {
    
    
	    public static void main(String[] args) {
    
    
	        MyObject object = new MyObject();
	        ThreadA threadA = new ThreadA(object);
	        threadA.setName("A");
	        ThreadB threadB = new ThreadB(object);
	        threadB.setName("B");
	
	        threadA.start();
	        threadB.start();
	    }
	}

对象级锁同步方法
从上图可以看出,A、B访问的不是同一个方法,但是也是同步执行。

正由于对象锁是对当前对象上锁,所以当使用两个对象进行上锁时,进行的是异步访问

	public class HasSelfPrivateNum {
    
    
	
	    private int num = 0;
	
	    synchronized public void addI(String username){
    
    
	        try {
    
    
	            if (username.equals("a")){
    
    
	                num = 100;
	                System.out.println("a set over!");
	                Thread.sleep(2000);
	            }else {
    
    
	                num = 200;
	                System.out.println("b set over!");
	            }
	            System.out.println(username + " num=" + num);
	        }catch (Exception e){
    
    
	            e.printStackTrace();
	        }
	    }
	}
	
	public class ThreadA extends Thread {
    
    
	    private HasSelfPrivateNum numRef;
	
	    public ThreadA(HasSelfPrivateNum numRef){
    
    
	        this.numRef = numRef;
	    }
	
	    @Override
	    public void run() {
    
    
	        numRef.addI("a");
	    }
	}
	
	public class ThreadB extends Thread {
    
    
	    private HasSelfPrivateNum numRef;
	
	    public ThreadB(HasSelfPrivateNum numRef){
    
    
	        this.numRef = numRef;
	    }
	
	    @Override
	    public void run() {
    
    
	        numRef.addI("b");
	    }
	}
	
	public class Run {
    
    
	    public static void main(String[] args) {
    
    
	        HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
	        HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();
	
	        ThreadA threadA = new ThreadA(numRef1);
	        threadA.start();
	        ThreadB threadB = new ThreadB(numRef2);
	        threadB.start();
	    }
	}

在这里插入图片描述
由上图可以看出,当监视器(锁)为通一类的不同对象时,方法调用是异步的。

	对象级锁的特殊例子:synchronized(String)是特例,因为JVM中有常量池的关系,所以字符串对象作为锁的情况下,不能够使用相同的字符串,即使是不同new
	出来的对象。

类级锁——synchronized修饰静态方法和synchronized(class)

由于Class类对象是单例的,所以对类对象进行上锁,即使是同一类的不同对象的锁,也是同步的。

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

	public class ThreadA extends Thread {
    
    
	    private HasSelfPrivateNum numRef;
	
	    public ThreadA(HasSelfPrivateNum numRef){
    
    
	        this.numRef = numRef;
	    }
	
	    @Override
	    public void run() {
    
    
	        numRef.addI("a");
	    }
	}
	
	public class ThreadB extends Thread {
    
    
	    private HasSelfPrivateNum numRef;
	
	    public ThreadB(HasSelfPrivateNum numRef){
    
    
	        this.numRef = numRef;
	    }
	
	    @Override
	    public void run() {
    
    
	        numRef.addI("b");
	    }
	}
	
	public class Run {
    
    
	    public static void main(String[] args) {
    
    
	        HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
	        HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();
	
	        ThreadA threadA = new ThreadA(numRef1);
	        threadA.start();
	        ThreadB threadB = new ThreadB(numRef2);
	        threadB.start();
	    }
	}

在这里插入图片描述
由上图可以看出,当锁改为类级锁时,即使监视器为一个类的两个对象,但依然时同步调用。

对象级锁与类级锁不同

对对象级锁和类级锁进行上锁时不同的,对象级锁只会对对象级锁进行同步控制,而不能够对类级锁进行同步控制;反之亦然。

	public class Service {
    
    
	    synchronized public static void printA(){
    
    
	        try {
    
    
	            System.out.println("threadName=" + Thread.currentThread().getName() + " on " + System.currentTimeMillis() + "进入printA");
	            Thread.sleep(3000);
	            System.out.println("threadName=" + Thread.currentThread().getName() + " on " + System.currentTimeMillis() + "离开printA");
	        }catch (Exception e){
    
    
	            e.printStackTrace();
	        }
	    }
	
	    synchronized public static void printB() {
    
    
	        System.out.println("ThreadName=" + Thread.currentThread().getName() + "on" + System.currentTimeMillis() + "进入printB");
	        System.out.println("ThreadName=" + Thread.currentThread().getName() + "on" + System.currentTimeMillis() + "离开printB");
	    }
	
	    synchronized public void printC() {
    
    
	        System.out.println("ThreadName=" + Thread.currentThread().getName() + "on" + System.currentTimeMillis() + "进入printC");
	        System.out.println("ThreadName=" + Thread.currentThread().getName() + "on" + System.currentTimeMillis() + "离开printC");
	    }
	}
	
	public class ThreadA extends Thread {
    
    
	    private Service service;
	
	    public ThreadA(Service service) {
    
    
	        this.service = service;
	    }
	
	    @Override
	    public void run() {
    
    
	        service.printA();
	    }
	}
	
	public class ThreadB extends Thread {
    
    
	    private Service service;
	
	    public ThreadB(Service service) {
    
    
	        this.service = service;
	    }
	
	    @Override
	    public void run() {
    
    
	        service.printB();
	    }
	}
	
	public class ThreadC extends Thread {
    
    
	    private Service service;
	
	    public ThreadC(Service service) {
    
    
	        this.service = service;
	    }
	
	    @Override
	    public void run() {
    
    
	        service.printC();
	    }
	}
	
	public class Run {
    
    
	    public static void main(String[] args) {
    
    
	        Service service = new Service();
	        Service service1 = new Service();
	        ThreadA threadA = new ThreadA(service);
	        threadA.setName("A");
	        threadA.start();
	
	        ThreadB threadB = new ThreadB(service1);
	        threadB.setName("B");
	        threadB.start();
	
	        ThreadC threadC = new ThreadC(service);
	        threadC.setName("C");
	        threadC.start();
	    }
	}

在这里插入图片描述
由上图可以看出,A、B线程是同步关系,而与C为异步关系。

synchronized锁的属性

  1. synchronized是可重入锁,可以重复请求上锁。
  2. 当存在父子类继承关系时,子类完全可以通过锁重入调用父类同步方法。
  3. 出现异常锁会自动释放,但suspend()方法和sleep(millis)方法被调用后不释放锁
  4. 只要锁的对象不改变,运行结果就是同步的(即使对象属性改变)。

volatile关键字

volatile关键字与synchronized关键字的作用相同,都是保持可见性,原子性和禁止指令重排。用法就是作为关键字,放在类型之前。
具体的在JVM中进行详细了解。

猜你喜欢

转载自blog.csdn.net/qq_44157349/article/details/121252560