• 面试题:请写出线程同步相关的方法,以银行账号存储款为例


    一.该面试题主要考察多线程中的synchronized或者Lock的使用

     * 线程同步 :使用同步方法,实现线程同步
     * 同步synchronized方法的对象监视锁为this,当前对象
     * 多个线程使用同一把锁,如果线程安全必需确保:多个线程使用的是同一个this对象(Runnable适用于共享同一对象[如:this],如果Thread继承就会有问题[推荐使用Runnable])
     * 所有访问此对象方法的线程都在方法外等待,都会判断同步锁,降低效率,但确保线程安全问题
     * java的每个对象都有一个内置锁,当用synchronized关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,线程需要获得内置锁,否则就处于阻塞状

    synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类

    此例子中使用synchronized关键字:

    private synchronized void makeWithdraw(int amount){...}

    二.上代码

    BankAccount.java

     1 public class BankAccount {
     2     //余额
     3     private int balance = 500;
     4     //查询
     5     public int getBalance(){
     6         return balance;
     7     }
     8     //取款
     9     public void withdraw(int amount){
    10         balance = balance - amount;
    11     }
    12     //存款
    13     public void deposit(int amount){
    14         balance = balance + amount;
    15     }
    16 }

     SyncMethod.java

     1 /**
     2  * 此线程类实现Runnable接口
     3  * 线程同步 :使用同步方法,实现线程同步
     4  * 同步synchronized方法的的对象监视锁为this,当前对象
     5  * 多个线程使用同一把锁,如果线程安全必需确保:多个线程使用的是同一个this对象
     6  * 所有访问此对象方法的线程都在方法外等待,都会判断同步锁,降低效率,但确保线程安全问题
     7  * */
     8 public class SyncMethod implements Runnable {
     9     // 所有Thread多线程线程都共享Runnable(接口对象)和account对象
    10     private BankAccount account = new BankAccount();
    11     @Override
    12     public void run() {
    13         for (int i = 0; i < 5; i++) {// 总共取款5次
    14             makeWithdraw(100); // 每次取款100
    15             if (account.getBalance() < 0) {
    16                 System.out.println("☆" + Thread.currentThread().getName()+ "   透支了!");
    17             }
    18         }
    19     }
    20 
    21     /**
    22      * makeWithdraw 账户取款
    23      * @param amount取款金额
    24      * 打印log记录取款过程
    25      * */
    26     private synchronized void makeWithdraw(int amount){
    27         if(account.getBalance() >= amount){//如果余额足够则取款
    28             System.out.println("☆" + Thread.currentThread().getName() + "   准备取款!");
    29             try {
    30                 Thread.sleep(500);
    31             } catch (InterruptedException e) {
    32                 System.out.println(Thread.currentThread().getName() + "   准备取款,等待0.5s线程中断!" + e.getMessage());
    33             }
    34             account.withdraw(amount);
    35             System.out.println("☆" + Thread.currentThread().getName() + "   完成" + amount + "取款!余额为" + account.getBalance());
    36         }else{//余额不足则提示
    37             System.out.println("☆" + "余额不足以支付" + Thread.currentThread().getName() + amount + "   的取款,余额为" + account.getBalance());
    38         }
    39     }
    40 }

    TreadSyncTest.java

     1 public class TreadSyncTest {
     2 
     3     /*
     4         Junit不适合多线程并发测试。
     5         因为线程还在激活状态的时候,Junit已经执行完成。
     6         在Junit的TestRunner中,它没有被设计成搜寻Runnable实例,
     7         并且等待这些线程发出报告,它只是执行它们并且忽略了它们的存在。
     8         综上,不可能在Junit中编写和维护多线程的单元测试。
     9      */    
    10     /*
    11         @Test
    12         public void test() {
    13         }
    14     */
    15     
    16     public static void main(String[] args) {
    17         //实现Runnable:所有Thread多线程线程都共享Runnable(接口对象)
    18         //NoSync target =new NoSync();
    19         SyncMethod target = new SyncMethod();
    20         //创建罗密欧和朱丽叶两个线程实现取款(同时)
    21         Thread romeoThread = new Thread(target);
    22         romeoThread.setName("罗密欧");
    23         Thread julietThread = new Thread(target);
    24         julietThread.setName("朱丽叶");
    25         //调用Thread对象的start()方法,启动线程,执行run()方法(OS)
    26         romeoThread.start();
    27         julietThread.start();
    28     }
    29 }

     运行结果:

    正常运行结果1:
    ☆罗密欧   准备取款!
    ☆罗密欧   完成100取款!余额为400
    ☆罗密欧   准备取款!
    ☆罗密欧   完成100取款!余额为300
    ☆罗密欧   准备取款!
    ☆罗密欧   完成100取款!余额为200
    ☆罗密欧   准备取款!
    ☆罗密欧   完成100取款!余额为100
    ☆罗密欧   准备取款!
    ☆罗密欧   完成100取款!余额为0
    ☆余额不足以支付朱丽叶100   的取款,余额为0
    ☆余额不足以支付朱丽叶100   的取款,余额为0
    ☆余额不足以支付朱丽叶100   的取款,余额为0
    ☆余额不足以支付朱丽叶100   的取款,余额为0
    ☆余额不足以支付朱丽叶100   的取款,余额为0.
    
    正常运行结果2:
    ☆罗密欧   准备取款!
    ☆罗密欧   完成100取款!余额为400
    ☆罗密欧   准备取款!
    ☆罗密欧   完成100取款!余额为300
    ☆罗密欧   准备取款!
    ☆罗密欧   完成100取款!余额为200
    ☆罗密欧   准备取款!
    ☆罗密欧   完成100取款!余额为100
    ☆朱丽叶   准备取款!
    ☆朱丽叶   完成100取款!余额为0
    ☆余额不足以支付朱丽叶100   的取款,余额为0
    ☆余额不足以支付朱丽叶100   的取款,余额为0
    ☆余额不足以支付朱丽叶100   的取款,余额为0
    ☆余额不足以支付罗密欧100   的取款,余额为0
    ☆余额不足以支付朱丽叶100   的取款,余额为0
    
    正常运行结果3:
    ☆罗密欧   准备取款!
    ☆罗密欧   完成100取款!余额为400
    ☆罗密欧   准备取款!
    ☆罗密欧   完成100取款!余额为300
    ☆罗密欧   准备取款!
    ☆罗密欧   完成100取款!余额为200
    ☆朱丽叶   准备取款!
    ☆朱丽叶   完成100取款!余额为100
    ☆朱丽叶   准备取款!
    ☆朱丽叶   完成100取款!余额为0
    ☆余额不足以支付朱丽叶100   的取款,余额为0
    ☆余额不足以支付朱丽叶100   的取款,余额为0
    ☆余额不足以支付朱丽叶100   的取款,余额为0
    ☆余额不足以支付罗密欧100   的取款,余额为0
    ☆余额不足以支付罗密欧100   的取款,余额为0

    如果不用synchronized关键字的结果:

    ☆罗密欧   准备取款!
    ☆朱丽叶   准备取款!
    ☆朱丽叶   完成100取款!余额为300
    ☆罗密欧   完成100取款!余额为300
    ☆罗密欧   准备取款!
    ☆朱丽叶   准备取款!
    ☆朱丽叶   完成100取款!余额为100
    ☆朱丽叶   准备取款!
    ☆罗密欧   完成100取款!余额为100
    ☆罗密欧   准备取款!
    ☆罗密欧   完成100取款!余额为0
    ☆余额不足以支付罗密欧100   的取款,余额为0
    ☆余额不足以支付罗密欧100   的取款,余额为0
    ☆朱丽叶   完成100取款!余额为-100
    ☆朱丽叶   透支了!
    ☆余额不足以支付朱丽叶100   的取款,余额为-100
    ☆朱丽叶   透支了!
    ☆余额不足以支付朱丽叶100   的取款,余额为-100
    ☆朱丽叶   透支了!

    此博文转载并整理于:http://blog.csdn.net/typa01_kk/article/details/46738459

  • 相关阅读:
    c3p0用c3p0-config.xml方式配置 和 用spring配置时一点小区别
    mysql配置
    tomcat监控工具Probe
    tomcat配置
    jsp页面动态包含和静态包含
    servlet3的文件上传
    System.currentTimeMillis()和new Date().getTime()比较
    js-html2canvas导出html图片 导出pdf
    html中使用富文本编辑器
    Vue----vue-resource
  • 原文地址:https://www.cnblogs.com/DreamDrive/p/7726072.html
Copyright © 2020-2023  润新知