java线程之线程同步

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java线程之线程同步相关的知识,希望对你有一定的参考价值。

关于线程的安全问题,这里举一个很简单的银行取钱的问题:

  • 用户输入账号,密码,系统判断是否匹配
  • 用户输入取钱金额
  • 系统判断取钱金额是否合理,是否大于余额数
  • 如果取钱金额小于等于余额,取钱成功,大于则失败

当两个用户使用同一个账号取钱时,相当于两个线程并发取钱,则可能出现错误。

出现错误的原因是因为run()方法不具有同步安全性。为了解决这个问题,java多线程支持引入同步监视器,使用同步监视器的格式如下:

sysnchronized(boj){

..........

}

其运行逻辑是:加锁>修改>释放锁

synchronized关键字可以修饰方法,代码块,不能修饰构造器,成员变量

如下几种情况会释放同步监视器

  1. 当前执行线程的同步方法或者同步代码块执行结束
  2. 当前执行线程的同步方法或者同步代码遇到break,return终止了该方法或者代码块
  3. 当前执行线程的同步方法或者同步代码的程序中执行了同步监视器对象的wait()方法

如下几种情况不会释放同步监视器

  1. 线程执行同步方法或者同步代码块时,程序调用了Thread.sleep(),Thread.yield()方法
  2. 线程执行代码块时,其他线程执行该线程的suspend()方法将该线程挂起

这里放一个银行取钱的小例子:

package com.tc.test;

public class Acount {
    private String accountNo;
    private double balance;
    
    
    public Acount(String accountNo, double balance) {
        super();
        this.accountNo = accountNo;
        this.balance = balance;
    }
    
    public String getAccountNo() {
        return accountNo;
    }
    public void setAccountNo(String accountNo) {
        this.accountNo = accountNo;
    }
    public double getBalance() {
        return balance;
    }
//    public void setBalance(double balance) {
//        this.balance = balance;
//    }
    public synchronized void draw(double drawAmount){
        if( balance > drawAmount ){
            System.out.println(Thread.currentThread().getName()
                    +"取钱成功"+drawAmount);
            try{
                Thread.sleep(1);
            } catch(InterruptedException e){
                e.printStackTrace();
             }
            //修改余额
            balance-=drawAmount;
            System.out.println("余额为:"+balance);
        }else{
            System.out.println("取钱失败,余额不足!!!");
        }
    }

}
package com.tc.test;

public class DrawThread extends Thread {
    private Acount account;
    private double drawAmount;
    public DrawThread(String name , Acount account, double drawAmount) {
        super(name);
        this.account = account;
        this.drawAmount = drawAmount;
    }
    public void run(){
        account.draw(drawAmount);
    }
    
}
package com.tc.test;

public class DrawTest {

    public static void main(String[] args) {
            Acount ac=new Acount("123",1000);
            new DrawThread("第一个",ac,800).start();
            new DrawThread("第二个",ac,800).start();
    }

}

同步锁

从jdk1.5开始,java提供了一种功能更强大的线程同步机制————通过显示定义同步锁对象来实现同步,在这种机制下,同步锁由Lock对象充当。

 Lock是控制对个线程对共享资源的进行访问的工具,通常锁提供了对共享资源的独占访问,每次只能有一个线程对lock加锁,线程在访问共享资源之前必须先获得Lock对象

java8新增了新型的StampedLock类,为读写提供了三种锁模式:Writing,ReadOptimistic,Reading 。

死锁

当两个线程互相等待对方释放同步监视器时就会发生死锁,java虚拟机并没有检测,也没有采取措施来处理死锁,一旦出现死锁,整个程序不会发生任何异常,也不会有任何提示,只是所有线程处于阻塞状态。

以上是关于java线程之线程同步的主要内容,如果未能解决你的问题,请参考以下文章

Java基础之多线程

Java之线程,常用方法,线程同步,死锁

java基础入门-多线程同步浅析-以银行转账为样例

Java并发编程之线程安全线程通信

JAVA之线程同步的三种方法

Java多线程之线程同步