• Java基础教程——线程同步


    线程同步

    synchronized:同步的

    例:取钱

    不做线程同步的场合,假设骗子和户主同时取钱,可能出现这种情况:

    • 【骗子】取款2000:账户余额1000
    • 【户主】取款2000:账户余额1000
    • 结果是社会财富增加1000,银行不开心。

    代码如下所示:

    // 账户类
    class Account {
    	private int accountBalance = 2000;
    	public void withdraw(String userName, int amount) {
    		System.out.println(userName + "===in===");
    		if (accountBalance >= amount) {
    			try {
    				Thread.sleep(500);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			accountBalance -= amount;// 取钱
    			System.out.println(userName + "取款" + amount + ",余额:" + accountBalance);
    		} else {
    			System.out.println(userName + "企图取款" + amount + ",但余额只有:" + accountBalance);
    		}
    		System.out.println(userName + "===out===");
    	}
    }
    class MyThread extends Thread {
    	@Override
    	public void run() {
    		取钱Demo.act.withdraw(getName(), 1000);
    	}
    }
    public class 取钱Demo {
    	static Account act = new Account();// 账户就一份
    	public static void main(String[] args) {
    		MyThread t1 = new MyThread();
    		MyThread t2 = new MyThread();
    		t1.setName("户主");
    		t2.setName("骗子");
    		t2.start();
    		t1.start();
    	}
    }
    

    想要银行开心,就需要对线程进行同步处理,避免出现重复取款的情况。


    线程同步

    方法一:锁对象

    	public void withdraw(String userName, int amount) {
    		synchronized (this) {
              ……
            }
    	}
    

    如果是静态方法,没有this,则是锁住【类名.class】

    	public static void withdraw(String userName, int amount) {
    		synchronized (Account.class) {
              ……
            }
    	}
    

    方法二:锁方法

    当方法被调用时,调用线程必须获得当前对象的锁,否则将等待下去。
    方法结束后,锁会被释放。

    public synchronized void withdraw(String userName, int amount) {...}
    

    方法三:ReentrantLock重入锁

    ReentrantLock是java.util.concurrent.locks.Lock接口的一个实现类。(reentrant:[rɪ'entrənt]再进去)

    一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大(可以中断、定时)。

    API文档上建议的用法:

    建议总是 立即实践,使用 lock 块来调用 try,在之前/之后的构造中,最典型的代码如下: 
    
     class X {
       private final ReentrantLock lock = new ReentrantLock();
       // ...
    
       public void m() { 
         lock.lock();  // block until condition holds
         try {
           // ... method body
         } finally {
           lock.unlock()
         }
       }
     }
    
    

    应用示例:

    import java.util.concurrent.locks.ReentrantLock;
    // 账户类
    class Account {
    	private int accountBalance = 2000;
    	private ReentrantLock lock = new ReentrantLock();
    	public void withdraw(String userName, int amount) {
    		synchronized (Account.class) {
    			lock.lock();
    			try {
    				System.out.println(userName + "===in===");
    				if (accountBalance >= amount) {
    					Thread.sleep(500);
    					accountBalance -= amount;// 取钱
    					System.out.println(userName + "取款" + amount + ",余额:" + accountBalance);
    				} else {
    					System.out.println(userName + "企图取款" + amount + ",但余额只有:" + accountBalance);
    				}
    				System.out.println(userName + "===out===");
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			} finally {
    				lock.unlock();
    			}
    		}
    	}
    }
    

    练习:买票

    (未做线程同步,请实现线程同步)

    import java.util.Random;
    public class 卖票 {
    	public static void main(String[] args) {
    		// 一个Runnable实例对象
    		SellTicket st = new SellTicket();
    		// 创建三个线程对象
    		Thread t1 = new Thread(st, "窗口1");
    		Thread t2 = new Thread(st, "窗口2");
    		Thread t3 = new Thread(st, "窗口3");
    		// 启动线程
    		t1.start();
    		t2.start();
    		t3.start();
    	}
    }
    class SellTicket implements Runnable {
    	// 定义票数
    	private int tickets = 100;
    	private void sell() {
    		if (tickets > 0) {
    			// 模拟售票过程
    			try {
    				Thread.sleep(100);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			String name = Thread.currentThread().getName();
    			System.out.println(name + "正在出售第" + (tickets--) + "张票");
    		}
    	}
    	@Override
    	public void run() {
    		while (tickets > 0) {
    			sell();
    			// 模拟空闲过程
    			try {
    				Thread.sleep(new Random().nextInt(11) * 100);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }
    
  • 相关阅读:
    tp5.带标签的缓存 创建和清除 测试
    web消息推送-goesay
    链接生成二维码-PHP
    微信退款
    tp5 日志文件名称问题
    PHP 获取访问来源
    PHP苹果内购二次验证的那些巨坑
    Linux 按时间批量删除文件命令(删除N天前文件)
    利用WkHtmlToPdf,把H5 转成PDF
    phpstorm2018永久激活方法---安装包激活
  • 原文地址:https://www.cnblogs.com/tigerlion/p/11179366.html
Copyright © 2020-2023  润新知