Java中Volatile关键字解决Android中的线程BUG!
发布日期:2021-07-01 00:00:25 浏览次数:3 分类:技术文章

本文共 5463 字,大约阅读时间需要 18 分钟。

前言

在android 中最常见的就是线程操作

最近在调试小票打印机器的时候 开启了一个线程随时准备着打印小票
结果出现了bug 在打印的时候 不能控制打印的次数 还有线程执行的顺序

在徘徊了几天后,在查阅资料时看到了java中的关键字 Volatile

Volatile 特性

当一个变量定义为 volatile 之后,将具备两种特性:

1.保证此变量对所有的线程的可见性,这里的“可见性”,如本文开头所述,当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。但普通变量做不到这点,普通变量的值在线程间传递均需要通过主内存(详见:Java内存模型)来完成。

2.禁止指令重排序优化。有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置),只有一个CPU访问内存时,并不需要内存屏障;(什么是指令重排序:是指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理)。

volatile 性能:

volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。

代码片

首先在初始化变量的时候 我们给一个volatile 修饰的boolean值

volatile boolean printFlag = false;

在我们拿到打印数据的线程中 我们将变量printFlag置为true

if (bean != null && bean.size() > 0) {           mOrderDetailBeans = bean;           //拿到数据 设置为true           printFlag = true;       }

然后我们开一个线程 无线等待数据 拿到数据就打印小票

new Thread(() -> {            Looper.prepare();            try {                while (true) {                                      if (mUsbDriver != null) {                        try {                        	//这里修饰Volatile 为true才走打印数据                              while (printFlag) {                                ShowPrintClick();                                Log.e(TAG, " === >>> 执行打印数据");                            }                        } catch (Exception e) {                            Message message = new Message();                            message.what = 4;                            message.obj = e.getMessage();                            mMyHandler.sendMessage(message);                        }                    } else {                        Thread.sleep(1000);                    }                }            } catch (Exception e) {                e.printStackTrace();            }            Looper.loop();        }).start();

ShowPrintClick 方法代码

try {            Message message = Message.obtain();            message.what = 4;            message.obj = "极客多功能售药机";            mMyHandler.sendMessage(message);            if (printFlag && mOrderDetailBeans.size() > 0) {                mUsbDriver.write(PrintCmd.SetReadZKmode(0));                mUsbDriver.write(PrintCmd.PrintFeedDot(30));                StringBuffer header = new StringBuffer();                header.append("                 鑫斛药庄八店 " + lineEnd);                header.append("           买药买放心 放心到鑫斛药庄 " + lineEnd);                header.append("------------------------------------------------" + lineEnd);                header.append(lineEnd);                header.append("购买日期:" + getNowDate() + lineEnd);                header.append("订单编号:" + ordersNum);                mUsbDriver.write(PrintCmd.PrintString(header.toString(), 0));                OrderDetailBean orderDetailBean;                StringBuffer body = new StringBuffer();                int totalNum = 0;                double totalPrice = 0;                for (int i = 0; i < mOrderDetailBeans.size(); i++) {                    orderDetailBean = mOrderDetailBeans.get(i);                    body.append(lineEnd);                    body.append("药品名称:" + (("").equals(orderDetailBean.getGoodsName()) ? "" : orderDetailBean.getGoodsName()) + lineEnd);                    body.append("生产厂家:" + (("").equals(orderDetailBean.getManufacturer()) ? "" : orderDetailBean.getManufacturer()) + lineEnd);                    body.append("规格:" + (("").equals(orderDetailBean.getSpec()) ? "" : orderDetailBean.getSpec()) + lineEnd);                    body.append("批号:" + (("").equals(orderDetailBean.getBatchNum()) ? "" : orderDetailBean.getBatchNum()) + lineEnd);                    body.append("有效期:" + (("").equals(orderDetailBean.getExpireDateTime()) ? "" : orderDetailBean.getExpireDateTime()) + lineEnd);                    body.append("数量:" + orderDetailBean.getQuantity() + lineEnd);                    body.append("金额:" + orderDetailBean.getAmount());                    body.append(lineEnd);                    //这里是计算购买的总商品数                    totalNum += orderDetailBean.getQuantity();                    //累加总消费                    totalPrice = CalcUtils.addDouble(totalPrice, orderDetailBean.getAmount());                }                mUsbDriver.write(PrintCmd.PrintString(body.toString(), 0));                //打印总数量和总价格                StringBuffer total = new StringBuffer();                total.append("合计数量:" + totalNum + lineEnd);                total.append("合计金额:" + totalPrice + lineEnd);                mUsbDriver.write(PrintCmd.PrintString(total.toString(), 0));                body.append(lineEnd);                //打印地址                StringBuffer address = new StringBuffer();                address.append("------------------------------------------------" + lineEnd);                address.append("本小票为重打印,不做开票依据" + lineEnd);                address.append("地址:重庆市九龙坡区石坪桥正街2号1-2、3#" + lineEnd);                address.append("电话:023-72223575" + lineEnd);                address.append("药品是特殊商品,一经售出概不退换");                mUsbDriver.write(PrintCmd.PrintString(address.toString(), 0));                mUsbDriver.write(PrintCmd.PrintFeedline(5));                mUsbDriver.write(PrintCmd.PrintCutpaper(1));                //打印完了 再把printFlag设置为false 意思为只执行一次打印数据                printFlag = false;            }        } catch (Exception e) {            Message message = Message.obtain();            message.what = 4;            message.obj = e.getMessage();            mMyHandler.sendMessage(message);        }

总结

温故而知新,可以为师矣!

哈哈哈~

转载地址:https://lvshichunqiu.blog.csdn.net/article/details/113130480 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:java.net.SocketTimeoutException: connect timed out 解决方法
下一篇:Android Rxjava+Okhttp+Retrofit实现单文件下载功能

发表评论

最新留言

留言是一种美德,欢迎回访!
[***.207.175.100]2024年04月22日 03时29分56秒