Java 多线程(Thread) 同步(synchronized) 以及 wait, notify 相关 [实例介绍]

场景描述

有一家很大的商场,在某市有几个商品配送中心,并有几家分店,这家商场经营很多的商品,平时运营情况是这样的:

根据各分店的商品销售情况,给分店配送相应需求量的商品;并上架到分店指让的位置,供客户选购。

客户选择自己需要的商品,然后到收银台交钱打包;

然后到一天的某个时候分店管理员(经理等),开始统计当前的销售情况,并计划向商品配送中心订购各商品的配送量;

场景模拟

1. 商场类;

public class StoreMart {
    //商场这一天剩余的商品量
    private static Map<String, Integer> goodsMap = new HashMap<String, Integer>();
    private static Map<String, Integer> priceMap = new HashMap<String, Integer>();

    static {
        goodsMap.put("ap", 1000);
        goodsMap.put("bp", 500);
        goodsMap.put("cp", 2000);

        priceMap.put("ap", 20);
        priceMap.put("bp", 50);
        priceMap.put("cp", 30);
        //...
    }

    //选择商品
    //pn 商品名称
    //num 选购数量
    public synchronized void selGoods(String name,String pn, int num) {
        int total = getNum(pn);
        int remain = total - num;
        goodsMap.put(pn, remain);

        //保存用户购物车信息
    }

    //新增商品数据
    public synchronized void putGoods(String pn, int num) {
        int total = getNum(pn);
        total = total + num;
        goodsMap.put(pn, total);
    }

    public static int getNum(String pn) {
        int num = 0;

        if (goodsMap.containsKey(pn)) {
            num = goodsMap.get(pn);
        }

        return num;
    }

    //结算
    public void settleGoods(Map<String, Integer> goods) {
        //....
    }
}

用户购物类:

public class UserShop implements Runnable {
    private static StoreMart sm = new StoreMart();
    private final String name;
    private final Map<String, Integer> sgoods = new HashMap<String, Integer>();

    public UserShop(String name) {
        this.name = name;
    }

    public void add(String pn, int num) {
        sgoods.put(pn, num);
    }

    public void run() {
        for(Map.Entry<String, Integer> entry:sgoods.entrySet()){
            sm.selGoods(entry.getKey(), entry.getValue());

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        sm.settleGoods(sgoods);
        System.out.println(this.name + " 购物完毕! ");
    }

    public static void main(String[] args) {
        UserShop u1 = new UserShop("NameA");
        u1.add("ap",1);
        u1.add("bp",2);
        Thread t1 = new Thread(u1);

        UserShop u2 = new UserShop("NameB");
        u1.add("ap",4);
        u1.add("bp",5);
        Thread t2 = new Thread(u2);

        t1.start();
        t2.start();
    }
}

以上摸拟的是商品数量都充足的情况;

摸拟了两个客户 各购买了 不同数量的商品 ap, bp;

但是如果万一用户多了起来,如果有几个客户一下子要选购同一个商品很多的量;就会出问题了;

使用 wait(), notify()

修改 下 StoreMart 类:

//选择商品
//pn 商品名称
//num 选购数量
public synchronized void selGoods(String name,String pn, int num) {
    int total = getNum(pn);
    while (total < num) {
        System.out.println(pn + "商品量不够");
        this.wait();
    }
    int remain = total - num;
    goodsMap.put(pn, total);

    //保存用户购物车信息
}

//新增商品数据
public synchronized void putGoods(String pn, int num) {
    int total = getNum(pn);
    total = total + num;
    goodsMap.put(pn, total);
    this.notify();
}

在UserShop 的 run 方法里,添加商品不足补货的语句:

public void run() {
    for(Map.Entry<String, Integer> entry:sgoods.entrySet()){
        sm.selGoods(entry.getKey(), entry.getValue());

        try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        //发现商品不够了
        sm.putGoods("ap", 100);
        sm.putGoods("bp", 200);
        System.out.println(this.name + " 购物完毕! ");
}

这样 当商场里的 某商品量不足时,NameA 或 NameB 就得处于等待状态,就是要wait() 到等待集合,等待被 notify() 换醒;

synchronized, wait, notify说明

1. wait;

1>. 用于 当本 线程不能成立时,放弃当前占用的锁;

2>. wait 必须要在 synchronized 所在的代码块内;

3>. wait 是 Object 的方法;

4>. 一般要处理 while 循环 体内; 用于重新判断条件是否满足;

2. synchronized 一般结束多线程环境下,用于处理 占用 共享资源的问题;

3. notify 用于 唤醒 wait() 的线程;

wait 与 sleep 的区别

wait 针对的是 当前 synchronized 所在块的机锁:

1>. synchronized 如果 标识一个 普通类,那机锁就为当前 所在对象的 锁;

2>. synchronized 如果 标识一个 静态类,那机锁 就为所在类的类锁;

3>. 如果是 synchronized (obj) ,则 锁 为 obj 对象; 一般这个锁可以定义为 0 长度的 byte 数组,比较经济;

sleep 针的是当前 Thread;

sleep 一般是定时执行的;

wait 是需要竞争的,能否执行,得看本次锁是否分配给了它,本且条件是否满足;

interrupt() 都可以中止 sleep 或 wait 的暂停状态;如果线程 A 要中止 线程 B,则调用 线程B实例的 interrupt() 方法,当 线程B处于 wait(),sleep(),join() 时,就会抛出 InterruptedException 异常,在 catch 块执行 return ,即可正常结束线程;

时间: 2024-11-03 05:43:29

Java 多线程(Thread) 同步(synchronized) 以及 wait, notify 相关 [实例介绍]的相关文章

Java多线程的同步机制(synchronized)

一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在 java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池 等待队列中). 取到锁后,他就开始执行同步代码(被synchronized修饰的代码):线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中 等待的某个线程就可以拿到锁执行同步代码了.这样就保证了同步代码在统一时刻只有一个线程在执行. 众所周知,在Java多线程

Java多线程之二(Synchronized)

常用API method 注释 run() run()方法是我们创建线程时必须要实现的方法,但是实际上该方法只是一个普通方法,直接调用并没有开启线程的作用. start() start()方法作用为使该线程开始执行:Java虚拟机调用该线程的 run 方法. 但是该方法只能调用一次,如果线程已经启动将会抛出IllegalThreadStateException异常. yield() yield()方法让出CPU并且不会释放锁,让当前线程变为可运行状态,所以CPU下一次选择的线程仍可能是当前线程.

Java多线程4:synchronized锁机制

脏读 一个常见的概念.在多线程中,难免会出现在多个线程中对同一个对象的实例变量进行并发访问的情况,如果不做正确的同步处理,那么产生的后果就是"脏读",也就是取到的数据其实是被更改过的. 多线程线程安全问题示例 看一段代码: public class ThreadDomain13 { private int num = 0; public void addNum(String userName) { try { if ("a".equals(userName)) {

浅谈Java多线程的同步问题 【转】

多线程的同步依靠的是对象锁机制,synchronized关键字的背后就是利用了封锁来实现对共享资源的互斥访问. 下面以一个简单的实例来进行对比分析.实例要完成的工作非常简单,就是创建10个线程,每个线程都打印从0到99这100个数字,我们希望线程之间不会出现交叉乱序打印,而是顺序地打印. 先来看第一段代码,这里我们在run()方法中加入了synchronized关键字,希望能对run方法进行互斥访问,但结果并不如我们希望那样,这是因为这里synchronized锁住的是this对象,即当前运行线

浅谈Java多线程的同步问题

多线程的同步依靠的是对象锁机制,synchronized关键字的背后就是利用了封锁来实现对共享资源的互斥访问. 下面以一个简单的实例来进行对比分析.实例要完成的工作非常简单,就是创建10个线程,每个线程都打印从0到99这100个数字,我们希望线程之间不会出现交叉乱序打印,而是顺序地打印. 先来看第一段代码,这里我们在run()方法中加入了synchronized关键字,希望能对run方法进行互斥访问,但结果并不如我们希望那样,这是因为这里synchronized锁住的是this对象,即当前运行线

JAVA多线程线程同步问题

线程同步 在多线程的编程环境下,可能看着没有问题的代码在运行几千上万或者更多次后,出现了一些看着很奇怪的问题,出现这样的问题的原因就是可能会有两个或者更多个线程进入了同一块业务处理代码中导致了判断失效.为了解决这个问题,JAVA引入了同步监视器来解决这个问题.同步监视器的通用方法就是同步代码块,也就是给一块代码加了同步锁. package cn.test.hf; import java.math.BigDecimal; /** * 模拟取钱操作 */public class RunnableTe

JAVA多线程Thread VS Runnable详解

要求 必备知识 本文要求基本了解JAVA编程知识. 开发环境 windows 7/EditPlus 演示地址 源文件 进程与线程 进程是程序在处理机中的一次运行.一个进程既包括其所要执行的指令,也包括了执行指令所需的系统资源,不同进程所占用的系统资源相对独立.所以进程是重量级的任务,它们之间的通信和转换都需要操作系统付出较大的开销. 线程是进程中的一个实体,是被系统独立调度和分派的基本单位.线程自己基本上不拥有系统资源,但它可以与同属一个进程的其他线程共享进程所拥有的全部资源.所以线程是轻量级的

java多线程系列之 synchronized

一.synchronized基本原理 二.基本规则 第一条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞. class MyRunable implements Runnable { @Override public void run() { synchronized(this) { try { for (int i = 0; i

探Java多线程Thread类和Runnable接口之间的联系

首先复习一下Java多线程实现机制,Java实现多线程方法有如下这么几种: 1.继承了(extends)Thread类 2.实现了(implements)Runnable接口 也就是说  有如下两种情况 情况1: 继承Thread类.重写其方法run() .    然后new之.调用Start()方法 1 public class TestThread 2 { 3 private int i; 4 public static void main(String[] args) 5 { 6 // T