Java线程同步详解

时间:2021-08-08
本文章向大家介绍Java线程同步详解,主要包括Java线程同步详解使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

一、多线程运行问题

1、各个线程是通过竞争CPU时间而获得运行机会的
2、各线程什么时候得到CPU时间,占用多久,是不可预测的
3、一个正在运行着的线程在什么地方被暂停是不确定的

二、线程同步

为了解决上述问题,确保共享对象在同一时间只允许被一个线程访问,即线程同步,可以使用synchronized和lock来实现。

三、synchronized的使用方式

1、修饰一个代码块,被修饰的代码块称为同步代码块,作用范围是大括号{}括起来的代码;
2、修饰一个方法,被修饰的方法称为同步方法,其作用范围是整个方法;
3、修饰一个静态方法,作用范围是整个静态方法;

例程(银行存取款)

Class Bank

package com.imooc.bank;

public class Bank {
    private String account;//账号
    private int balance;//账户余额

    public Bank(String account,int balane){
        this.account=account;
        this.balance=balane;
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

    @Override
    public String toString() {
        return "Bank[账号:"+account+",余额:"+balance+"]";
    }
    //存款
    public synchronized void saveAccount(){
        //获取当前的账户余额
        int balance=getBalance();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //修改金额,存100元
        balance+=100;
        //修改账余额
        setBalance(balance);
        //输出存款后的账户余额
        System.out.println("存款后的账户余额为:"+balance);
    }
    public void drawAccount(){
        synchronized (this){
            //获得当前账户余额
            int balance=getBalance();
            balance=balance-200;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            setBalance(balance);
            System.out.println("取款后的账户余额:"+balance);
        }
    }
}

Class DrawAccount 取款

package com.imooc.bank;

public class DrawAccount implements Runnable{
    Bank bank;
    public DrawAccount(Bank bank){
        this.bank=bank;
    }
    @Override
    public void run() {
        bank.drawAccount();
    }
}

Class SaveAccount 存款

package com.imooc.bank;

public class SaveAccount implements Runnable{

    Bank bank;
    public SaveAccount(Bank bank){
        this.bank=bank;
    }
    @Override
    public void run() {
        bank.saveAccount();
    }
}

Class Test 测试类

package com.imooc.bank;

public class Test {
    public static void main(String[] args) {
        //创建账户,给定余额为10000
        Bank bank=new Bank("1001",1000);
        //创建线程对象
        SaveAccount sa=new SaveAccount(bank);
        DrawAccount da=new DrawAccount(bank);
        Thread save=new Thread(sa);
        Thread draw=new Thread(da);
        save.start();
        draw.start();
        try {
            draw.join();
            save.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(bank);
    }
}

经过测试,当不加入synchronized实现线程同步时,存款取款的账户余额会发生错误,
这里使用线程延时模拟了线程被打断的情况,当存款没完全进行完时打断了此线程进行取款
此时取款后的余额不正确。

当加入了线程同步后,此问题得到了解决。

lock的使用方式

1、synchroized同步的时候,其中一条线程用完会自动释放锁,而Lock需要手动释放,如果不手动释放,可能造成死锁
2、使用synchronized如果其中一个线程不释放锁,那么其他需要获取锁的线程会一直等待下取,直到使用完释放或者出现异常,
而Lock可以使用响应中断或者使用规定等待时间的锁
3、synchronized无法得知是否获取到锁,而Lock可以做到
4、用ReadWriteLock可以提高多个线程进行读操作的笑了

lock简单的使用方法

//创建lock对象
Lock lock=new ReentrantLock();
//手动加锁
lock.lock();
 try{
    // 处理
 }catch(Exception ex){
     // 捕获异常
}finally{
     // 释放锁
     lock.unlock();   
 }

你以为的极限,也许只是别人的起点

原文地址:https://www.cnblogs.com/LengDing/p/15115001.html