• 线程系列2---线程同步


    2013-12-23 10:31:55 

    1. 由于系统的线程调度很随机(其实也是按照一定的策略来调度的,但对于程序来说是随机的,毕竟是无法由程序控制),所以当多个线程访问共享资源时就有可能产生线程同步问题。

    2. 为了解决线程的同步问题,可以使用如下几种方式:

        2.1 同步代码块,用synchronized(obj) { }将需要同步的代码括起来,obj指的是同步监视器,含义是线程在开始执行这段代码之前需要先获得同步监视器的锁。

              1> 任何时刻,只有一个线程可以获得对同步监视器的锁定,当同步代码执行完成后释放锁。

              2> 虽然Java程序允许使用任何对象作为同步监视器,但通常推荐使用可能被并发访问的共享资源充当同步监视器。

        2.2 同步方法:指的是用synchronized修饰的方法,无需显式的指定同步监视器,因为同步监视器就是this,使用同步方法可以构造线程安全的类。线程安全的类有如下特征:

              1> 该类的对象可以被多个线程同时安全的访问;

              2> 每个线程调用该对象的任意方法均能得到正确的结果;

              3> 每个线程调用该对象的任意方法之后,该对象状态依然保持在合理状态;

    3. 可变类的线程安全是以降低程序的运行效率为代价的,所以:

              1> 不要对线程安全类的所有方法都进行同步,只对那些会改变竞争资源的方法同步;

              2> 如果可变类有两种运行环境,那么最好为这个类提供不同的版本,如StringBuffer和StringBuilder。

    4. 释放同步监视器的锁定:

              1> 当前线程的同步方法或者同步代码块执行结束即释放该同步监视器;

              2> 当前线程在同步方法或者同步代码块中遇到了break、return或者未捕获的Error or Exception终止该代码块;

              3> 当前线程执行同步代码块或者同步方法时,程序执行了同步监视器对象的wait()方法。

    5. 以下情况,线程不会释放同步监视器:

              1> 执行时,程序调用sleep(),yield()放暂停当前线程的执行;

              2> 执行时,其他线程调用该线程的suspend()方法将该线程挂起;

    6. Java 5开始提供了一种更强大的线程同步机制---通过显式定义同步锁对象实现同步,同步锁使用Lock对象充当。

    7. 死锁:当两个线程互相等待对方释放同步监视器时就会发生死锁。一旦出现死锁,程序既不会发生异常,也不会给出任何提示,只是所有线程处于Block状态无法继续。

    8. 线程通信:

        传统的线程通信:可借助Object对象的三个方法wait(), notify()和notifyAll()。这三个方法不属于Thread类,且必须由同步监视器对象来调用。
        关于三个方法的解释:
              wait(): 导致当前线程暂停,释放锁,直到其他线程调用该同步监视器的notify()方法来唤醒这个线程。
              notify(): 唤醒此同步监视器上等待的单个线程,如果有多个,则会随机唤醒一个。只有当前线程放弃对该同步监视器的锁定,才会执行被唤醒的线程。
              notifyAll():唤醒在此同步监视器上等待的所有线程,只有当前线程放弃对该同步监视器的锁定,才会执行被唤醒的线程。

    9. 线程同步实例:模拟存款和取款,要求:存一次,取一次,不允许连存或者连取,代码如下:

    Account.java

     1 public class Account {
     2     private String mAccountNo;
     3     private int mBalance;
     4     private boolean flag;
     5    
     6     public Account() {
     7     }
     8    
     9     public Account(String accountNo, int balance) {
    10         this.mAccountNo = accountNo;
    11         this.mBalance = balance;
    12     }
    13    
    14     public int getBalance() {
    15         return mBalance;
    16     }
    17    
    18     public synchronized void draw(int drawAccount) {
    19         try {
    20             if (!flag) {
    21                 this.wait();
    22             } else {
    23                 System.out.println(Thread.currentThread().getName() + " 取款 :" + drawAccount);
    24                 mBalance -= drawAccount;
    25                 System.out.println("--------余额是 :" + mBalance);
    26                 flag = false;
    27                 notifyAll();
    28             }
    29         } catch (InterruptedException e) {
    30             e.printStackTrace();
    31         }
    32     }
    33    
    34     public synchronized void despoit(int despoiteAccount) {
    35         try {
    36             if (flag) {
    37                 this.wait();
    38             } else {
    39                 System.out.println(Thread.currentThread().getName() + " 存款 :" + despoiteAccount);
    40                 mBalance += despoiteAccount;
    41                 System.out.println("=======余额是 :" + mBalance);
    42                 flag = true;
    43                 notifyAll();
    44             }
    45         } catch (InterruptedException e) {
    46             e.printStackTrace();
    47         }
    48     }
    49 }

    DespoiteThread.java

     1 public class DespoiteThread extends Thread {
     2     Account mAccount;
     3     private int mDespioteAccount;
     4   
     5     public DespoiteThread(String name, Account account, int despioteAccount) {
     6         super(name);
     7         mAccount = account;
     8         mDespioteAccount = despioteAccount;
     9     }
    10   
    11     @Override
    12     public void run() {
    13         for (int i = 0; i < 100; i++) {
    14             mAccount.despoit(mDespioteAccount);
    15         }
    16     }
    17 }

    DrawThread.java

     1 public class DrawThread extends Thread {
     2     Account mAccount;
     3     private int mDrawAccount;
     4   
     5     public DrawThread(String name, Account account, int drawAccount) {
     6         super(name);
     7         mAccount = account;
     8         mDrawAccount = drawAccount;
     9     }
    10   
    11     @Override
    12     public void run() {
    13         for (int i = 0; i < 100; i++) {
    14             mAccount.draw(mDrawAccount);
    15         }
    16     }
    17 }

    MainTest.java

    1 public class MainTest {
    2     public static void main(String[] args) {
    3         Account account = new Account("DaWei", 1000);
    4         new DrawThread("取款者", account, 600).start();
    5         new DespoiteThread("存款者", account, 800).start();
    6     }
    7 }

     

  • 相关阅读:
    当jsp中Springboot之登录模块探索(含Token,验证码还有数据库
    当php内容的内存分页不,如何更快的使用head的分段式处理方式
    Java 开发 2021 年发生的的一些自我总结和教训,即使反省
    总结这些年php函数中遇到的绊脚石,告别以后面试的现场尴尬
    使用 WSDL 指定的标准 SOAP 消息格式
    ORACLE数据库导出表,字段名,长度,类型,字段注释,表注释语句
    oracle中start with和connect by的用法理解
    关于RabbitMQ以及RabbitMQ和Spring的整合
    Vue.js——基于$.ajax实现数据的跨域增删查改
    HTML表格跨行、跨列操作(rowspan、colspan)
  • 原文地址:https://www.cnblogs.com/wlrhnh/p/3487032.html
Copyright © 2020-2023  润新知