• synchronized同步方法《二》


    1.synchronized方法和锁对象

    (1)、验证线程锁的是对象

    代码如下:

    1.1创建一个MyObject类:

    package edu.ymm.about_thread4;
    
    public class Myobject {
    	
    	public void methodA() {
    		try {
    			System.out.println("begin methodA threadName=" 
    					+ Thread.currentThread().getName());
    			Thread.sleep(5000);
    			System.out.println("end");
    		}catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    1.2创建一个ThreadA类:

    package edu.ymm.about_thread4;
    
    public class ThreadA extends Thread {
    
    	private Myobject myobject;
    	public ThreadA(Myobject myobject) {
    		super();
    		this.myobject = myobject;
    	}
    	
    	@Override
    	public void run() {
    		super.run();
    		myobject.methodA();
    	}
    	
    	
    }
    

     1.3创建一个ThreadB类:

    package edu.ymm.about_thread4;
    
    public class ThreadB extends Thread {
    
    	private Myobject myobject;
    	public ThreadB(Myobject myobject) {
    		super();
    		this.myobject = myobject;
    	}
    	
    	@Override
    	public void run() {
    		super.run();
    		myobject.methodA();
    	}
    	
    	
    }
    

      1.4创建一个Test测试类:

    package edu.ymm.about_thread4;
    
    public class Test {
    
    	public static void main(String[] args) {
    
    		Myobject myobject = new Myobject();
    		ThreadA threadA = new ThreadA(myobject);
    		threadA.setName("a");
    		
    		ThreadB threadB  =new ThreadB(myobject);
    		threadB.setName("b");
    		threadA.start();
    		threadB.start();
    	}
    
    }
    

       1.5结果如下:

    更改MyObject类后:

    package edu.ymm.about_thread4;
    
    public class Myobject {
    	
    	synchronized public void methodA() {
    		try {
    			System.out.println("begin methodA threadName=" 
    					+ Thread.currentThread().getName());
    			Thread.sleep(5000);
    			System.out.println("end");
    		}catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    }

     其他三个类不变,结果如下:

     

    通过上面的代码得到结论,调用关键字synchronized声明的方法一定是排队运行的。另外需要牢牢记住“共享”,因为只有共享资源的读写访问才需要同步化,如果不是共享资源,那么根本就没有同步的必要!

     下面我们来看另一种情况:

    (2)、其他方法被调用的效果,以及查看Lock锁对象的效果:

    2.1继续创建一个MyObject类:

    package edu.ymm.about_thread4;
    
    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 (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    	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 (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    }
    

     2.2继续创建一个ThreadA类:

    package edu.ymm.about_thread4;
    
    public class ThreadA extends Thread {
    
    	private Myobject myobject;
    	public ThreadA(Myobject myobject) {
    		super();
    		this.myobject = myobject;
    	}
    	
    	@Override
    	public void run() {
    		super.run();
    		myobject.methodA();
    	}
    	
    	
    }
    

      2.3继续创建一个ThreadB类:

    package edu.ymm.about_thread4;
    
    public class ThreadB extends Thread {
    
    	private Myobject myobject;
    	public ThreadB(Myobject myobject) {
    		super();
    		this.myobject = myobject;
    	}
    	
    	@Override
    	public void run() {
    		super.run();
    		myobject.methodB();
    	}
    	
    	
    }
    

       2.4继续创建一个Test类:

    package edu.ymm.about_thread4;
    
    public class Test {
    
    	public static void main(String[] args) {
    
    		Myobject myobject = new Myobject();
    		ThreadA threadA = new ThreadA(myobject);
    		threadA.setName("a");
    		
    		ThreadB threadB  =new ThreadB(myobject);
    		threadB.setName("b");
    		threadA.start();
    		threadB.start();
    	}
    
    }
    

     结果如下:

    通过这个例子我们可以得到的结论是:

    (1)A线程先持有Object对象的Lock锁,B线程可以以异步的方式调用Object对象中的非synchronized类型的方法。

    (2)A线程先持有Object对象的Lock锁,B线程如果在这时调用Object对象中的synchronized类型的方法则需要等待,也就是同步!

     2.脏读

      在上面1的例子中已经实现多个线程调用同一个方法时,为了避免数据出现交叉的情况,使用synchronized关键字来进行同步。虽然在赋值时进行了同步。但是在取值时有可能出现一些意料不到的意外,这种情况就是脏读。发生脏读的情况是在读取实例变量时,此值已经被其他线程更改过了。

    (1)创建一个publicVar类:

    package edu.ymm.about_thread5;
    
    public class PublicVar {
    	public String username = "A";
    	public String password = "AA";
    	synchronized public void setValue(String username,String password) {
    		try {
    			this.username = username;
    			Thread.sleep(5000);
    			this.password = password;
    			System.out.println("setValue method thread name=" 
    								+ Thread.currentThread().getName()+ " username "
    								+ username + " password " + password);
    		}catch (InterruptedException e){
    			e.printStackTrace();
    		}
    	}
    	public void getValue() {
    		System.out.println("getValue method thread name=" 
    				+ Thread.currentThread().getName()+ " username "
    				+ username + " password " + password);
    	}
    }
    

     (2)创建一个ThreadA类:

    package edu.ymm.about_thread5;
    
    public class ThreadA extends Thread {
    
    	private PublicVar publicVar;
    	public ThreadA(PublicVar publicVar) {
    		super();
    		this.publicVar = publicVar;
    	}
    	
    	@Override
    	public void run() {
    		super.run();
    		publicVar.setValue("B", "BB");
    	}
    	
    	
    }
    

      (3)创建一个Test类:

    package edu.ymm.about_thread5;
    
    public class Test {
    
    	public static void main(String[] args) {
    		try {
    			PublicVar publicVar = new PublicVar();
    			ThreadA threadA = new ThreadA(publicVar);
    			threadA.start();
    			Thread.sleep(2000);
    			publicVar.getValue();
    		}catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    
    }
    

     执行结果如下:

    上述的结果中已经出现了脏读,username为B的密码成了“AA”。原因就是public void getValue()方法不是同步的,所以在任何时候都可以调用。解决办法就是加synchronized:

    package edu.ymm.about_thread5;
    
    public class PublicVar {
    	public String username = "A";
    	public String password = "AA";
    	synchronized public void setValue(String username,String password) {
    		try {
    			this.username = username;
    			Thread.sleep(5000);
    			this.password = password;
    			System.out.println("setValue method thread name=" 
    								+ Thread.currentThread().getName()+ " username "
    								+ username + " password " + password);
    		}catch (InterruptedException e){
    			e.printStackTrace();
    		}
    	}
    	synchronized public void getValue() {
    		System.out.println("getValue method thread name=" 
    				+ Thread.currentThread().getName()+ " username "
    				+ username + " password " + password);
    	}
    }
    

     改后结果为:

    可见,方法setValue()和方法getValue()被依次执行。通过这个例子不仅要知道脏读是通过synchronized解决的。还得明白以下几点:

      当A线程调用anyObject对象加入synchronized 关键字的X方法时,A线程就获得了x方法锁,更准确地讲,是获得了对象的锁,所以其他线程必须等A线程执行完毕才可以调用x方法,但B线程可以随意调用其他的非synchronized同步方法。

      当A线程调用anyobject对象加入synchronized关键字的X方法时,A线程就获得了X方法所在对象的锁,所以其他线程必须等A线程执行完毕才可以调用X方法,而B线程如果调用声明了synchronized关键字的非X方法时,必须等A线程将X方法执行完,也就是释放对象锁后才可以调用。这时A线程已经执行了一个完整的任务,也就是说usemame和password这两个实例变量已经同时被赋值,不存在脏读的基本环境。

      脏读一定会出现操作实例变量的情况下,这就是不同线程 “争抢”实例变量的结果。

  • 相关阅读:
    《Three.js 入门指南》3.1.1
    《Three.js 入门指南》3.1.1
    《Three.js 入门指南》3.1.1
    Spring 框架基础(04):AOP切面编程概念,几种实现方式演示
    微服务架构案例(01):项目技术选型简介,架构图解说明
    Java描述设计模式(15):责任链模式
    数据安全管理:RSA加密算法,签名验签流程详解
    Java描述设计模式(14):解释器模式
    SpringBoot2 配置多数据源,整合MybatisPlus增强插件
    SpringBoot2 整合 Drools规则引擎,实现高效的业务规则
  • 原文地址:https://www.cnblogs.com/youdiaodaxue16/p/10742818.html
Copyright © 2020-2023  润新知