zoukankan      html  css  js  c++  java
  • 【J2SE高速进阶】——多线程之synchronized

             

            我和老婆去银行取钱          

            有一天,和老婆打了个赌。如今我的银行账号里共同拥有5000块钱。我们去银行同一时候取钱,看我俩能不能同一时候取出5000来。。。。(PS:打赌的代价是:假设都能取出5000,那这10000块都给她买吃的!假设仅仅能取5000,嘿嘿。那就仅仅用着5000块给她买吃的~~~~怎么认为这条件有点怪怪的nie?)

            心动不如行动!

    她拿着存折去柜台取,我拿着银行卡去ATM机取。找了个合适的时机,我在输入好金额时,一直盯着那个teller的手。在一切准备就绪后敲回车的同一时候,我以迅雷不及掩耳之势按下了确定。

    结果是,我的ATM机唰唰唰吐出50张毛爷爷,老婆那边也得意洋洋的拿着5000块钱过来了。。。。
            “喂~醒醒了。”

            原来是在做梦~~不行,这可能行得通,我得试试!别不相信,我还用刚学的线程的知识验证了一下呢!      

    public class TestDrawMoney implements Runnable {
    	//说明:此例的背景是【多人】【同一时候】在【同一账户上】取款
    	//在统一账户danny的账户上取款
    	Depositor danny = new Depositor();
    	public static void main(String[] args) {
    		TestDrawMoney test=new TestDrawMoney();
    		//我取款的操作是一个线程draw1,我老婆取款的操作也是一个线程draw2
    		Thread draw1 = new Thread(test);
    		Thread draw2 = new Thread(test);
    		draw1.setName("我");
    		draw2.setName("我老婆");
    		//启动线程
    		draw1.start();
    		draw2.start();
    	}
        //重写run方法
    	public void run() {
    		danny.DrawMoney(5000);
    	}
    }
    
    //储户类
    class Depositor {
    	//剩余金额
    	private static int deposit = 5000;
    	//取钱方法
    	public void  DrawMoney(int money) {
    		if (money <= deposit) {
    			// 模拟银行吐钱过程
    			System.out.println(Thread.currentThread().getName() + "取款时:成功取款。" + money + "元!");
    			// 模拟账户扣款过程
    			deposit = deposit - money;
    		}else{
    			System.out.println(Thread.currentThread().getName()+"取款时:剩余金额不足!");
    		}		
    	}
    }

            假设银行取款的程序跟上面相似的话。那么当中一个线程(比方我老婆取钱)执行到“账户扣款”之前时,资源可能被还有一个线程(比方我取钱)抢过来执行,这时。第一个线程由于已经推断账户剩余金额充足。并且已经把钱给了我老婆,可是银行扣款执行之前,我取钱这个线程抢先执行。银行还没有扣款。所以在我取钱这个线程中,推断剩余金额也是充足的。


           您推測试结果怎么着?我俩还真都取出了5000!

           

              

           可惜学习了的线程中锁的机制后,这个发財梦被彻底打破了。

    。。

    public class TestDrawMoney implements Runnable {
    	// 说明:此例的背景是【多人】【同一时候】在【同一账户上】取款
    	// 在统一账户danny的账户上取款
    	Depositor danny = new Depositor();
    
    	public static void main(String[] args) {
    		TestDrawMoney test = new TestDrawMoney();
    		// 我取款的操作是一个线程draw1,我老婆取款的操作也是一个线程draw2
    		Thread draw1 = new Thread(test);
    		Thread draw2 = new Thread(test);
    		draw1.setName("我");
    		draw2.setName("我老婆");
    		// 启动线程
    		draw1.start();
    		draw2.start();
    	}
    
    	// 重写run方法
    	public void run() {
    		danny.DrawMoney(5000);
    	}
    }
    
    // 储户类
    class Depositor {
    	// 剩余金额
    	private static int deposit = 5000;
    	// 取钱方法
    	public void DrawMoney(int money) {
    		synchronized (this) {//这里为整个线程的操作加上了锁
    			if (money <= deposit) {
    				// 模拟银行吐钱过程
    				System.out.println(Thread.currentThread().getName() + "取款时:成功取款。" + money + "元!");
    				// 模拟账户扣款过程
    				deposit = deposit - money;
    			} else {
    				System.out.println(Thread.currentThread().getName+"取款时:剩余金额不足!");
    			}
    		}
    	}
    }

              跟第一个样例唯一不同之处,就是在取钱方法DrawMoney()中加了锁——synchronized,这样,当一个线程执行此方法时,这个对象就会被锁定。其它线程就无法执行此方法中被锁定的代码。

           执行结果是,仅仅能有一个人能取出钱来:

              或    (这两个线程,先运行的可以取出钱,但谁先谁后并不一定,所以会有两种结果。


           synchronized介绍

           当多个线程可能在同一时候訪问同样的资源时。就要考虑是否用synchronized。synchronized用来给对象、方法或者代码块加锁。

    同一时刻仅仅能有一个线程能够运行被锁代码,其它不论什么线程都不会打断它。必须等到当前正在运行被锁代码的线程运行完成,其它线程才干够运行。

           sychronized的实质是。在运行被锁方法或代码的过程中,锁定当前对象(上例中既然锁定了当前对象“danny”,那么肯定也锁定了当前对象中的静态变量“deposit”)。

           

           synchronized的使用方法

           1、用synchronized修饰方法。如上例中能够直接用synchronized修饰DrawMoney()的方法:    

    	public synchronized void DrawMoney(int money) {
    			if (money <= deposit) {
    				// 模拟银行吐钱过程
    				System.out.println(Thread.currentThread().getName() + "取款时:成功取款" + money + "元!");
    				// 模拟账户扣款过程
    				deposit = deposit - money;
    			} else {
    				System.out.println(Thread.currentThread().getName() + "取款时:剩余金额不足!

    "); } }

                假设一个对象有多个synchronized方法,仅仅要一个线程訪问了当中的一个synchronized方法,其他线程不能同一时候訪问这个对象中不论什么一个synchronized方法。

           假设两个线程想同一时候訪问这种方法。那么须要在两个实例对象中訪问。也就是说。不同对象实例中synchronized修饰的方法时互不干扰的。


             2、用synchronized锁住代码

           正如上文第二个样例。在方法中,用synchronized(this){ //代码片段}锁住同一时刻仅仅同意一个线程訪问的代码。

    同一时刻,其它线程不同意訪问此对象的被锁代码段。即运行到此代码段时。此当前对象会被锁住,其它线程不同意訪问。待当前线程运行完成,其它线程才干够訪问。


            synchronized修饰成员方法和synchronized(this)两种使用方法的含义是一样的。


            synchronized可能会带来的问题

            当把程序“锁”住时,还会带来其它问题——死锁。

            比方如今Danny和Maria两个人仅仅有一双筷子,想吃大餐的他们仅仅有抢到两仅仅筷子才干够开吃:       

    public class TestDeadLock2 implements Runnable {
    	public String name;      //姓名
    	static Object chopsticks1 = new Object();      //第一仅仅筷子
    	static Object chopsticks2 = new Object();      //第二仅仅筷子
    
    	public static void main(String[] args) {
    		System.out.println("抢筷子開始!");
    		TestDeadLock2 test1=new TestDeadLock2();
    		TestDeadLock2 test2=new TestDeadLock2();
    		test1.name="Danny";
    		test2.name="Maria";
    		Thread t1=new Thread(test1);
    		Thread t2=new Thread(test2);
    		t1.start();
    		t2.start();
    	}
    
    	public void run() {
    		if (name.equals("Danny")) {
    			synchronized (chopsticks1) {
    				try {
    					Thread.sleep(500);//这里为了放大效果。让Danny抢到筷子1时停顿一下
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    				synchronized (chopsticks2) {
    					System.out.println("Danny成功抢到两仅仅筷子。他要开吃啦!

    "); } } } else if (name.equals("Maria")) { synchronized (chopsticks2) { try { Thread.sleep(500);//这里为了放大效果。让Maria抢到筷子2时停顿一下 } catch (InterruptedException e) { e.printStackTrace(); } synchronized (chopsticks1) { System.out.println("Maria成功抢到两仅仅筷子。她要开吃啦!"); } } } } }

            样例中,Danny和Maria抢到每一仅仅筷子时都会把筷子握在手里,当Danny抢到筷子1,Maria抢到筷子2时。他们都在等对方让出筷子。互不相让,无限等待下去。这就发生了死锁。


            所以,用锁需慎重。涉及到同步时,一定要考虑这个线程是不是应该同步,加了同步,效率变低,不加同步,可能产生类似上例中数据不一致的现象。所以,在保证数据安全的情况下,同步区域(即被锁区域)越小越好。


  • 相关阅读:
    CodeForces
    CodeForces-1253B(贪心+模拟)
    WebFlux、Reactive编程特性
    redis-on-windows配置解释
    SpringBoot配置方式补充
    如何在 Ubuntu 20.04 上安装 Python Pip
    MySQL之1055错误
    CuckooSandbox
    Manjaro 20.0.1 Lysia 安装Googlepinyin
    Manjaro 20.0.1 Lysia 更新国内镜像源
  • 原文地址:https://www.cnblogs.com/cynchanpin/p/6808515.html
Copyright © 2011-2022 走看看