Java多线程03

线程同步

并发:多个线程访问同一个对象,造成数据不安全

三个例子

package com.guanxing.syn;

public class UnsafeTicket {
    public static void main(String[] args) {
        BuyTicket station = new BuyTicket();

        new Thread(station, "苦逼的我").start();
        new Thread(station, "牛逼的你").start();
        new Thread(station, "可恶的黄牛党").start();
    }
}

class BuyTicket extends Thread {
    //票
    private int ticketNums = 10;
    boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            buy();
        }
    }

    //买票的方法
    public void buy() {
        //判断是否有票
        if (ticketNums <= 0) {
            flag = false;
            return;
        }
        //模拟延时
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //买票
        System.out.println(Thread.currentThread().getName()+"抢到了第"+ticketNums--+"张票");
    }
}
package com.guanxing.syn;

//不安全的取钱
//两个人去银行取钱
public class UnsafeBank {
    public static void main(String[] args) {
        //账户
        Account account = new Account(100, "结婚基金");

        Drawing jay = new Drawing(account, 50, "jay");
        Drawing kelly = new Drawing(account, 100, "kelly");

        jay.start();
        kelly.start();
    }
}


//账户
class Account {
    int money;  //余额
    String name;  //卡名

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

//银行:模拟取款
class Drawing extends Thread {

    Account account;   //账户
    int drawingMoney;  //取了多少钱
    int nowMoney;  //手上的钱

    //构造器
    public Drawing(Account account, int drawingMoney, String name) {
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    //取钱
    @Override
    public void run() {
        //判断余额
        if (account.money-drawingMoney<0) {
            System.out.println(Thread.currentThread().getName()+"钱不够了!");
            return;
        }
        //模拟延迟 放大问题的发生性
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //卡内余额 = 余额 - 取的钱
        account.money -= drawingMoney;
        nowMoney += drawingMoney;

        //你卡里的钱
        System.out.println(account.name+"余额为:"+account.money);
        //你手里的钱
        System.out.println(this.getName()+"手里的钱为:"+nowMoney);
    }
}

/*
结婚基金余额为:50
结婚基金余额为:-50
jay手里的钱为:50
kelly手里的钱为:100
 */
package com.guanxing.syn;

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

public class UnsafeList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());  //9617 没有sleep  sleep5s 9999
    }
}

死锁

某一个同步块同时拥有 “ 两个以上对象的锁 ” 时,就可能发生 “ 死锁 ” 现象。

两个或多个线程都在等待对方释放资源,都停止运行的情况

产生死锁的四个必要条件(想办法打破一个就可避免!)

  • 互斥条件:一个资源每次只能被一个进程使用
  • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
  • 不剥夺条件:进程已获得的资源,在未使用完之前,不能抢行剥夺
  • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
package com.guanxing.syn;

//死锁:多个线程互相抱着对方需要的资源,形成僵持
public class DeadLock {
    public static void main(String[] args) {
        Makeup joey = new Makeup(0, "joey");
        Makeup kane = new Makeup(1, "kane");
        joey.start();
        kane.start();
    }
}

//口红
class Lipstick {
}

//镜子
class Mirror {
}

class Makeup extends Thread {
    //口红和镜子(static保证都只有一个)
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    //化妆者的选择和名字
    int choice;
    String girlName;

    //构造器
    public Makeup (int choice, String girlName) {
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //化妆的方法,互相持有对方的资源
    private void makeup() throws InterruptedException {
        if (choice == 0) {
            synchronized (lipstick) {//获得口红的锁
                System.out.println(this.girlName+"抢到口红!");
                Thread.sleep(1000);
                synchronized (mirror) {//抱着口红的锁想获得镜子的锁
                    System.out.println(this.girlName+"抢到镜子了!");
                }
            }
        } else {
            synchronized (mirror) {
                System.out.println(this.girlName+"抢到镜子!");
                Thread.sleep(2000);
                synchronized (lipstick) {
                    System.out.println(this.girlName+"抢到口红了!");
                }
            }
        }
    }
}

解决方法:同时只能拿着一把锁

//化妆的方法,互相持有对方的资源
private void makeup() throws InterruptedException {
    if (choice == 0) {
        synchronized (lipstick) {//获得口红的锁
            System.out.println(this.girlName+"抢到口红!");
            Thread.sleep(1000);
        }
        synchronized (mirror) {//抱着口红的锁想获得镜子的锁
            System.out.println(this.girlName+"抢到镜子了!");
        }
    } else {
        synchronized (mirror) {
            System.out.println(this.girlName+"抢到镜子!");
            Thread.sleep(2000);
        }
        synchronized (lipstick) {
            System.out.println(this.girlName+"抢到口红了!");
        }
    }
}

Lock(锁)

显示定义同步锁对象来实现同步,手动开启和关闭锁

java.util.concurrent.locks.Lock 接口是控制多个线程对共享资源进行访问的工具

ReentrantLock 类实现了 Lock(re-entrant 可重入锁)

Lock只有代码块锁,syn有代码块锁和方法锁

package com.guanxing.syn;

import java.util.concurrent.locks.ReentrantLock;

//测试Lock锁
public class TestLock {
    public static void main(String[] args) {
        MyLock myLock = new MyLock();

        new Thread(myLock).start();
        new Thread(myLock).start();
        new Thread(myLock).start();
    }
}

class MyLock implements Runnable {
    //票
    int ticketNum = 1000;
    //定义Lock锁
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                lock.lock(); //加锁
                if (ticketNum>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"-->"+ticketNum--);
                }else {
                    break;
                }
            }finally {
                //解锁
                lock.unlock();
            }

        }
    }
}
posted @ 2021-03-09 08:19  straightup  阅读(49)  评论(0编辑  收藏  举报