《JAVA300集》多线程_并发基础案例-day21

目录

一、并发与线程同步

二、锁机制

抢票案例

取钱案例

抢票案例优化版本

影院购票案例

抢票案例-面向对象思想

 三、并发容器


一、并发与线程同步

并发:同一个对象的多个线程同时操作。

并发有可能引发线程不安全的问题。

线程同步是一种等待机制,用于解决线程不安全问题。

当多个线程访问同一个对象,并且某些线程还想修改这个对象。这时候就可以通过线程同步方法。多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用

二、锁机制

为了保证数据在多线程访问中的正确性,在访问时加入了锁机制(synchronized),当一个线程获得对象的排它锁,就可以独占资源,其他线程必须等待,这个线程使用完成后释放锁。

使用锁机制有以下问题:(锁会导致性能下降

 使用方法: synchronized方法和synchronized块。

每个对象对应一把锁

难点:如何合适的用锁,使得锁不多也不少。同时兼顾安全和性能。

抢票案例

package Thread;

/*
*线程安全:在并发时保证数据的正确性,效率尽可能高
* synchronized
* 同步方法
*  */
public class SynTest01 {
    public static void main(String[] args) {
        SafeWeb12306 web = new SafeWeb12306();
        new Thread(web,"小黄").start();
        new Thread(web,"小王").start();
        new Thread(web,"小蒋").start();
    }
}

class SafeWeb12306 implements Runnable{
    private int ticketNums = 10;
    private boolean flag = true;

    @Override
    public void run() {
        while(flag){
            // 模拟抢票延时
            try{
                Thread.sleep(200);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            test();
        }
    }

    // 锁方法里面跟对象相关的资源,使得只能同时被1个线程访问
    public synchronized void test(){
        if(ticketNums<=0){
            flag = false;
            return;
        }
        System.out.println(Thread.currentThread().getName()+"抢到了"+ticketNums--+"号票");
    }
}

运行结果:

取钱案例

// 账户
public class Account {
    int money;
    String name;
    public Account(int money,String name){
        this.money = money;
        this.name = name;
    }
}

错误示例-

//模拟取钱(错误示例)
public class SynTest02 {
    public static void main(String[] args) {
        //账户
        Account account = new Account(100,"结婚礼金");
        SafeDrawing you = new SafeDrawing(account,80,"bad的你");
        SafeDrawing wife = new SafeDrawing(account,90,"happy的小蒋");
        you.start();
        wife.start();
    }
}

class SafeDrawing extends Thread{
    Account account;  //取钱的账户
    int drawingMoney; //取出的钱
    int packetTotal; //口袋里的钱

    public SafeDrawing(Account account,int drawingMoney,String name){
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    @Override
    public void run() {
        test();
    }

    //锁这个方法没有用,这个方法类似于取款机,应该去锁账户,因为是账户的对象
    public synchronized void test(){
        if(account.money-drawingMoney<0){
            return;
        }
        try{
            Thread.sleep(100);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        account.money -= drawingMoney;
        packetTotal += drawingMoney;
        System.out.println(this.getName()+"--账户 "+account.money);
        System.out.println(this.getName()+"--口袋的钱 "+packetTotal);
    }
}

锁方法,是锁方法的对象,方法的默认对象是this

 正确示例-

同步块- synchronized(obj){},obj称为同步监视器。  obj推荐使用共享资源。

package Thread;

//模拟取钱(正确示例)
public class SynBlockTest01 {
    public static void main(String[] args) {
        //账户
        Account account = new Account(1000,"结婚礼金");
        SynDrawing you = new SynDrawing(account,80,"bad的你");
        SynDrawing wife = new SynDrawing(account,90,"happy的小蒋");
        you.start();
        wife.start();
    }
}

class SynDrawing extends Thread{
    Account account;  //取钱的账户
    int drawingMoney; //取出的钱
    int packetTotal; //口袋里的钱

    public SynDrawing(Account account,int drawingMoney,String name){
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    @Override
    public void run() {
        test();
    }

    //目标锁定账户
    public void test(){
        //这个if可以提升代码执行效率
        if(account.money<=0){
            return;
        }
        synchronized (account) {
            if (account.money - drawingMoney < 0) {
                return;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            account.money -= drawingMoney;
            packetTotal += drawingMoney;
            System.out.println(this.getName() + "--账户 " + account.money);
            System.out.println(this.getName() + "--口袋的钱 " + packetTotal);
        }
    }
}

运行结果:

同步块可以精准地锁定目标对象。(这个例子中是account)

抢票案例优化版本

/*
 *线程安全:在并发时保证数据的正确性,效率尽可能高
 * synchronized
 * 同步块
 *  */
public class SynBlockTest03 {
    public static void main(String[] args) {
        SynWeb12306 web = new SynWeb12306();
        new Thread(web,"小黄").start();
        new Thread(web,"小王").start();
        new Thread(web,"小蒋").start();
    }
}

class SynWeb12306 implements Runnable{
    private int ticketNums = 10;
    private boolean flag = true;

    @Override
    public void run() {
        while(flag){
            // 模拟抢票延时
            try{
                Thread.sleep(200);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            //test();
            //falseTest();
            goodTest();
        }
    }

    public void test() {
        //synchronized只能锁一个资源
        synchronized (this) {
            if (ticketNums <= 0) {
                flag = false;
                return;
            }
            System.out.println(Thread.currentThread().getName() + "抢到了" + ticketNums-- + "号票");
        }
    }

    //漏锁了一个对象,导致线程依旧不安全
    public void falseTest() {
        synchronized ((Integer)ticketNums) {
            if (ticketNums <= 0) {
                flag = false;
                return;
            }
            System.out.println(Thread.currentThread().getName() + "抢到了" + ticketNums-- + "号票");
        }
    }

    //线程安全:尽可能锁定合理的范围,这样可以提升性能
    public void goodTest() {
        //双重检测,主要考虑的是临界值的问题
        if (ticketNums <= 0) { //考虑的是没有票的情况
            flag = false;
            return;
        }
        synchronized (this) {
            if (ticketNums <= 0) { //考虑的是最后1张票
                flag = false;
                return;
            }
            System.out.println(Thread.currentThread().getName() + "抢到了" + ticketNums-- + "号票");
        }
    }
}

影院购票案例

package Thread;

import java.util.ArrayList;
import java.util.List;

public class HappyCinema2 {
    public static void main(String[] args) {
        //影院可用的位置
        List<Integer> avaliable = new ArrayList<Integer>();
        avaliable.add(1);
        avaliable.add(2);
        avaliable.add(3);
        avaliable.add(4);

        //顾客需要的位置
        List<Integer> seats1 = new ArrayList<Integer>();
        seats1.add(1);
        seats1.add(2);
        List<Integer> seats2 = new ArrayList<Integer>();
        seats2.add(3);
        seats2.add(5);

        HCinema c = new HCinema(avaliable,"万达影院");
        new Thread(new HappyCustomer(c,seats1),"小张和小机智").start();
        new Thread(new HappyCustomer(c,seats2),"小赵").start();
        System.out.println("欢迎光临!");
    }
}

//顾客
class HappyCustomer implements Runnable{
    HCinema hinema;
    List<Integer> seats;

    public HappyCustomer(HCinema hinema, List<Integer> seats) {
        this.hinema = hinema;
        this.seats = seats;
    }

    @Override
    public void run() {
        synchronized (hinema){
            System.out.println(Thread.currentThread().getName()+" -想要购买的位置为:"+seats);
            boolean flag = hinema.bookTickets(seats);
            if(flag){
                System.out.println("出票成功 "+ Thread.currentThread().getName()+" -购买的位置为:"+seats+"\n");
            }else{
                System.out.println("出票失败 "+ Thread.currentThread().getName()+" -购买失败,没位置了");
            }
        }
    }
}

//影院
class HCinema{
    List<Integer> avaliable; //可用的位置
    String name;

    public HCinema(List<Integer> avaliable, String name) {
        this.avaliable = avaliable;
        this.name = name;
    }

    //购票
    public boolean bookTickets(List<Integer> seats){
        System.out.println(this.name+"当前可用位置为: " + avaliable);
        //两个容器的比较
        List<Integer> copy = new ArrayList<Integer>();
        copy.addAll(avaliable);
        //相减
        copy.removeAll(seats);
        //判断是否成功
        if(avaliable.size()-copy.size()!=seats.size()){
            return false;
        }else{
            avaliable = copy;
            return true;
        }
    }
}

运行结果:

抢票案例-面向对象思想

public class Happy12306 {
    public static void main(String[] args) {
        Web123066 w = new Web123066(2,"铁路局");
        new Passenger(w,"小张",2).start();
        new Passenger(w,"小机智",2).start();

    }
}

//乘客 (继承,作为Thread的子代理)
class Passenger extends Thread{
    int seats;

    public Passenger(Runnable target, String name, int seats) {
        super(target,name);
        this.seats = seats;
    }
}

//火车票网
class Web123066 implements Runnable{
    int  avaliable; //可用的位置
    String name;

    public Web123066(int avaliable, String name) {
        this.avaliable = avaliable;
        this.name = name;
    }

    @Override
    public void run() {
        Passenger p = (Passenger)Thread.currentThread(); //把当前线程强转为当前子类
        boolean flag = this.bookTickets(p.seats);
        if(flag){
            System.out.println("出票成功 "+ Thread.currentThread().getName()+" -购买位置数量为:"+p.seats);
        }else{
            System.out.println("出票失败 "+ Thread.currentThread().getName()+" -购买失败,没位置了");
        }
    }

    //购票
    public synchronized boolean bookTickets(int seats){
        //购票延迟
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(this.name+"当前可用位置为: " + avaliable);
        if(seats>avaliable){
            return false;
        }

        avaliable -=seats;
        return true;
    }
}

运行结果: 

Passenger p = (Passenger)Thread.currentThread(); //把当前线程强转为当前子类

 三、并发容器

java中有自带的并发容器,名为util.concurrent

import java.util.concurrent.*

import java.util.concurrent.CopyOnWriteArrayList;
/*
 * 并发安全操作容器
 * */
public class SynContainer {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        for(int i=0;i<1000;i++){
            new Thread(()-> list.add(Thread.currentThread().getName())).start();
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值