java中如何能避免过长的switch-case分支语句?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java中如何能避免过长的switch-case分支语句?相关的知识,希望对你有一定的参考价值。
要编写一个网络程序,socket连接。客户端传上来的都是一些状态码,比如100代表登录,101代表注册等等。这样的状态码大约有800多个
在服务器端,接收到不同的状态码,就要调用不同对象的不同方法,比如,登录就调用user.login(),注册就调用user.register(),等等.
这样,我使用switch-cash语句的话,这个类至少得2000多行。分支太多的话,效率会很低。
我考虑过使用配置文件加反射来实现。结果大量的反射处理同样把效率降得很低。
这种状况应该怎么处理呢?哪位大侠知道?
二楼的能不能详细一点,呵呵。想法不错,可是怎么实现呢,我读到末端,得到功能字符串,还是要使用反射执行啊?
sun官方说明,java中的if是经过效率优化的。
反射的效率是if语句的1/10。(就是说反射是很慢的)
之前做电信项目的时候我就遇到过。
使用if语句几百个分支的效率是每秒几万次。效率非常高。
(当然if中是有逻辑的。)
如果你确实有800个的话。
有一个提高效率的方法。
就是把if分组
if(组一)
if()...
else if(组二)
if()...
每组对应一个类。
类中都实现do(int status)方法。
把状态的if else都放到每个类中的do方法中写。
这样代码清晰。而且由于分了组
每组100个左右,这样效率就更高了。 参考技术A 大约有800多个,就是分组,不要担心效率 参考技术B 我觉得的话应该构建一个树,有1个根结点,10个子节点(表示0-9),每个子结点再分10个子节点,再分..最末端的节点一共构成800多个,每个都存着你需要的功能
然后 读取状态码的时候从第1位读取,按照读出的数字(0-9),分支到下一个子节点,然后读取第二个数字,再分支,一直下去..
这样的话 一个4位的状态码只用执行4次switch,每个switch中判断10个case.而switch-case只写一次,剩下就是递归..不知这样效率如何. 参考技术C 貌似没啥好办法...按数字范围再分分块吧。
167Java利用可重入锁避免并发下出现错误数据,并且避免死锁以及等待锁的时间过长
注意
本文只讲解使用可重入锁解决问题的方法,其他方案放在文末,也不考虑 select for update 的方案。
1.场景
我以医院的病房管理系统为例来说明可重入锁。
先放数据库的表结构:
-- 房间表
CREATE TABLE IF NOT EXISTS public.room
(
id bigint NOT NULL, -- 主键
room_no character varying(10) COLLATE pg_catalog."default", -- 房间号
remark character varying(50) COLLATE pg_catalog."default", -- 备注
sort_no integer, -- 排序
CONSTRAINT yqgl_room_pkey PRIMARY KEY (id)
)
-- 病床表
CREATE TABLE IF NOT EXISTS public.t_bed
(
id bigint NOT NULL, -- 主键
room_id bigint NOT NULL, -- 房间ID
user_id bigint NOT NULL, -- 病人ID
bed_no character varying(50) COLLATE pg_catalog."default", -- 床的编号
update_time timestamp without time zone NOT NULL, -- 更新时间
CONSTRAINT yqgl_bed_pkey PRIMARY KEY (id)
)
病床表里的数据是管理员提前按照房间ID和病床编号录入的。对于无人占用的病床记录,字段user_id设置成0。
在医院里每个病房的床位是有限的,护士需要在系统中选择有空床位的病房,来分配给新入院的病人入住。当系统分配完房间后还需要一系列后续操作,比如打印单据交给病人,或者给病人发送提示短信。医院有多名护士多台电脑来同时操作系统分配房间。这个时候并发条件下如果没有锁就有可能出现以下问题:
- 护士A发现101病房空着,点击按钮分配给病人A。
- 于此同时护士B也发现101病房空着,点击按钮分配给病人B。
- 服务器同时接到了两个请求并分配了两个线程A、B来同时处理两个请求。
- 线程A和B同时开启数据库事务。
- 线程A查询数据库发现101房间还有一个空床位,病人A当前没有占用床位。
- 同时线程B查询数据库也发现101房间还有一个空床位,病人B当前没有占用床位。
- 线程A更新数据库,把最后一个床位ID和病人A的ID关联。并执行后续操作。
- 线程B更新数据库,把最后一个床位ID和病人B的ID关联。并执行后续操作。
- 线程A提交数据库事务,返回给护士A成功提示。
- 线程B提交数据库事务,返回给护士B成功提示。
显然护士A的操作被护士B覆盖了。但是系统给出了有误导性的提示。更为严重的是,系统的后续操作会带来更多麻烦,比如打印出了错误的住院单,给病人发送了错误的提示短信。
2.必须要搞清楚锁的范围
首先说明,锁的范围要覆盖数据库事务的范围,即先加锁、再开启事务、然后提交或者回滚事务、最后解锁。如果把锁放在事务中(即先开启事务、再加锁、解锁、最后提交或者回滚事务),是没有效果的。为了弄清楚原因,我们先看数据库的四种隔离机制:
- read uncommited(读未提交)
- read commited (读已提交)
- repeatable read (可重复读)
- Serializable (串行化)
由于性能原因,几乎没有人使用 Serializable。为了数据正确性也几乎没有人使用 read uncommited(读未提交)。常用的是 read commited 和 repeatale read 。下面我们分开讨论:
2.1 read commited
如果我们把加锁的代码放到事务中,因为对数据库的更改未提交,很可能导致两个线程一前一后读取了旧版本的数据并做了修改,然后同时提交事务后,导致其中一个的数据被覆盖。
2.1 repeatable read
虽然可重复读能够让事务中的数据不受其他事务的影响,但是在本例中并不能解决问题。下面我们来演示以下:
- 线程A开启事务、获取锁、查询数据库检查确定可以更新、更新数据库、解锁,在提交前操作系统调度线程。CPU切换到线程B。
- 线程B开启事务、获取锁、查询数据库检查确定可以更新、更新数据库、解锁。因为线程A没有提交数据库,所以这里数据库读取的是旧快照。
- 线程AB提交事务,数据被其中一方覆盖。
3.解决方案
存放锁对象的类 LockUtils
import java.util.concurrent.locks.ReentrantLock;
public class LockUtils
public static final ReentrantLock BED_LOCK = new ReentrantLock();
为了保证锁覆盖事务,我把锁放到 Controller 层:
@RestController
@RequestMapping("/isolate/userInfo")
public class RoomController
/**
* 分配房间
*/
@PutMapping("/distributeRoom")
public R<Void> distributeRoom(@RequestBody Map<String, Object> param)
// 检查参数的代码,已忽略
R r = R.error();
// 使用重入锁,避免同一个房间被分配给多个人。
try
// 这里是最多等待十秒钟就放弃,提示系统用户稍后重试。
boolean flag = LockUtils.BED_LOCK.tryLock(10L * 1000L, TimeUnit.MILLISECONDS);
if (flag)
r = roomService.distributeRoom(patientId, roomId);
else
r = R.error("系统繁忙,请稍后重试。");
catch (InterruptedException e)
e.printStackTrace();
finally
// 确保锁被释放
LockUtils.YQGL_USER_INFO_LOCK.unlock();
return r;
业务类 RoomService,注意这里的方法声明了事务,确保锁的范围覆盖事务的范围。里面的业务使用伪代码编写。
@Service
public class RoomServiceImpl implements RoomService
@Override
@Transactional(rollbackFor = Exception.class)
public R<Void> distributeRoom(Long patientId, Long roomId)
1. 查询数据库,判断房间有没有床位,判断病人当前有没有床位。
2. 如果房间没有床位或者病人已经占用了其他床位,终止执行,给出用户提示。
3. 如果房间有床位并且病人没有床位,就更新数据库,把床位分配给病人。
4. 执行其他相关操作。
以上是关于java中如何能避免过长的switch-case分支语句?的主要内容,如果未能解决你的问题,请参考以下文章
使用大型数据库时,如何避免 UITableView 中的自定义单元格加载时间过长
167Java利用可重入锁避免并发下出现错误数据,并且避免死锁以及等待锁的时间过长
167Java利用可重入锁避免并发下出现错误数据,并且避免死锁以及等待锁的时间过长
java使用jxl操作一百万数据到excel中,分sheet写入(一个sheet写五万)。如何避免内存溢出?
如果 Android 中的 HttpGet 操作持续时间过长,如何避免出现错误 10053 (WSAECONNABORTED)?