tryLock的一个使用示例
就算是有几年工作经验的,如果没有专业的训练,也不一定能写出一手线程安全的代码,对于一般的web开发而言,多线程相关的部分都封装在web server里了,而平时的业务开发不涉及这些高级特性。这是一件好事,因为这样将程序员的注意力都集中在与公司收入直接相关的业务逻辑层,而不需要关注比较复杂的技术层面,但是对程序员个人提升上也有不利的一面,通用的复杂技术都被封装了,程序员工作的技术性也相应降低。所以这需要我们在业余时间不断充电,训练,并且在工作上把握一切提升自我的机会。
以前做过一个爬虫项目,每天要抓取大量的商品数据,但是一些知名电商网站往往会设置各种限制,其中一个限制就是ip黑名单,网站会识别一些有爬虫机器特征的访问来源ip,并计入黑名单,下次爬取就会设置各种关卡,其中一个应对方法就是动态变更ip,方法如下:
public int changeIPBySh() {
try {
logger.error("execute shell command ");
Worker.execute();
} catch (Exception e) {
logger.error("Error when process changeIp work" + e.getMessage(), e);
}
return 0;
}
原有程序是通过shell脚本变更ip地址的,由于是多线程运行的,经常会有多个线程同时执行脚本的情况,我们很快就会想到加锁。
public int changeIPBySh() {
try {
synchronized(this){
logger.error("execute shell command ");
Worker.execute();
}
} catch (Exception e) {
logger.error("Error when process changeIp work" + e.getMessage(), e);
}
return 0;
}
但是这并没有什么卵用,当一个线程执行完脚本解锁后,原来在对象锁等待的线程会获得锁,进而再次执行脚本,这导致一些无谓的ip变更,而且在变更过程中,会影响其他线程的内容抓取。我们的目标是保证在同一时刻只有一个线程变更ip,变更时,新的线程不再等待释放锁,也不重复执行变更脚本。tryLock就可以实现这一目标。
private int changeIPByShdd() {
boolean captured = lock.tryLock();
try {
if (captured) {
logger.error("execute shell command ");
Worker.execute();
} else {
Thread.sleep(sleepTime);
}
} catch (Exception e) {
logger.error("Error when process changeIp work" + e.getMessage(), e);
} finally {
if (captured) {
lock.unlock();
}
}
return 0;
}
使用tryLock,如果获取了锁则返回true,执行脚本,如果没有,则立即返回false,线程进入休眠。而使用synchronized则会一直等待锁的释放,在语义tryLock提供了一种更适合当前场景的机制。
从广泛的层面而言,使用synchronized,一旦发生死锁,只能重启应用,而tryLock却可以避免一些偶发的死锁。synchronized是在jvm层实现的,发生了异常会自动释放锁,但是tryLock是在代码层面实现的,需要自己释放锁:
finally {
if (captured) {
lock.unlock();
}
}
- 如果你想深刻理解ASP.NET Core请求处理管道,可以试着写一个自定义的Server
- 路面能发电,智慧交通不遥远
- 小程序:企鹅帝国身后,微信帝国正悄悄露出冰山一角!
- ASP.NET MVC路由扩展:路由映射
- 如何改善遗留的代码库
- ASP.NET的路由系统:根据路由规则生成URL
- ASP.NET Core 1.0中实现文件上传的两种方式(提交表单和采用AJAX)
- 通过3个Hello World应用来了解ASP.NET 5应用是如何运行的(1)
- 工业X.0将至 企业数字化转型该怎么做?
- 通过3个Hello World应用来了解ASP.NET 5应用是如何运行的(2)
- 通过3个Hello World应用来了解ASP.NET 5应用是如何运行的(3)
- 为什么说2018年互联网创业机会将变少
- ASP.NET MVC Controller激活系统详解:IoC的应用[上篇]
- ASP.NET Core的配置(1):读取配置信息
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- JavaWeb——JavaScript精讲之事件监听机制与表单校验案例实战
- Java——扩展概念(匿名内部类、包装类、装箱与拆箱、数据类型的转换)
- Java——接口的基本总结(基本定义、使用接口定义标准、工厂设计模式、代理设计模式、抽象类与接口的区别)
- JavaWeb——HTML表单标签详解(input、label、select、textarea)
- Java——设计辅助概念(final关键字、对象多态性基本概念)
- JavaWeb——JavaScript精讲之DOM、BOM对象与案例实战(动态添加删除表格)
- JavaWeb——JavaScript精讲之ECMAScript标准(基本语法、JavaScript对象)
- JavaWeb——HTML基本标签详解及案例实战(文件标签、文本标签、图片标签、列表标签、链接标签、块标签、语义化标签、表格标签)
- JavaWeb——web概念概述(静态资源与动态资源)、HTML概念概述
- Java——内部类使用总结(基本概念、定义内部类、static定义内部类、方法中定义内部类)
- Java——泛型基本总结(通配符、泛型接口、泛型方法)
- Java——类图、时序图、用例图
- Java——四种访问控制权限及Java命名规范
- Java——static关键字总结(含义、定义属性或方法、使用时机)
- Java——try catch finally异常的捕获及处理逻辑实例详解大全