学习java基础关键字之synchronized和volatile
来源:互联网 发布:google chrome mac 编辑:程序博客网 时间:2024/06/05 22:36
一、synchronized,先看一个简单的示例:
public class Counter {
public static int count = 0;
public static void add() {
//这里延迟1毫秒,使得结果明显
count++;
//这里每次运行的值都有可能不同,可能为1000
System.out.println("运行结果:Counter.count=" + Counter.count);
}
public static void main(String[] args) {
//同时启动1000个线程,去进行i++计算,看看实际结果
for (int i = 0; i < 1000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
Counter.add();
}
}).start();
}
}
}
上面的main方法执行过后的结果如下:
运行结果:Counter.count=1
运行结果:Counter.count=3
运行结果:Counter.count=2
运行结果:Counter.count=4
。。。
运行结果:Counter.count=997
运行结果:Counter.count=1000
运行结果:Counter.count=999
并且每一次运行输出的结果都不一样,为什么会这样呢,相信很多人都清楚了,这里我就不说了(多线程的问题)
而我现在要做的是让这个示例输出的结果是从1-1000,那么就只需要用synchronized声明add方法就可以。
public class Counter {
public static int count = 0;
public synchronized static void add() {
//这里延迟1毫秒,使得结果明显
count++;
//这里每次运行的值都有可能不同,可能为1000
System.out.println("运行结果:Counter.count=" + Counter.count);
}
public static void main(String[] args) {
//同时启动1000个线程,去进行i++计算,看看实际结果
for (int i = 0; i < 1000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
Counter.add();
}
}).start();
}
}
}
上面的main方法执行过后的结果如下:
运行结果:Counter.count=1
运行结果:Counter.count=2
运行结果:Counter.count=3
运行结果:Counter.count=4
。。。
运行结果:Counter.count=998
运行结果:Counter.count=999
运行结果:Counter.count=1000
synchronized这个关键字我用的比较多,比如我在做公司的一个项目中就用来控制过资源的争夺问题,项目一块购,我在这个项目中只做了一个接口,购买支付接口,这里有一个问题是需要解决的:在多人同时购买同一个商品的时候,需要控制每个用户得到的云购码。
首先是云购码的生成:9位数字,如100000168,假如这一期这个商品的总价是6000元,则一共有从100000001到100006000这6000个云购码,这些云购码不是按顺序发放,而是随机发放,就是说,用户用一块钱来买这个商品,就会随机得到一个云购码。
这里我要做的是,假如N个用户同时购买这个商品,怎么样才能保证每个人随机得到的云购码都是不同的,没有重复的。
最简单的做法就是让N个买相同商品的人进行排队,像上面的示例用synchronized声明add方法就可以。
但是问题来了,性能会非常的低,因为来网站购买商品的人非常多,而且购买的商品也不尽全是相同的。如果像上面那样控制,效率非常低。
在这里,我写出了我的做法:
1、创建请求类RequestModel
public class RequestModel {
private String sign; // 标志唯一
private boolean isOverTime; // 标示是否超时
private long systemTime;//系统时间
// set/get方法
}
2、资源控制类SourceControl
public class SourceControl{
// 用ReentrantLock和Condition控制线程
public static final ReentrantLock reentrantLock = new ReentrantLock();
public static final Condition condition = reentrantLock.newCondition();
//资源
public static Map<String, List<RequestModel>> map = new HashMap<String, List<RequestModel>>();
/**
* 把请求放进资源库
* @param key 关键字
* @param requestModel
* @return 返回第一次放入的key
*/
public synchronized static String putKey(String key, RequestModel requestModel) {
String tmp = containsKey(key);
if (!"0".equalsIgnoreCase(tmp)) {
List<RequestModel> list = map.get(tmp);
list.add(requestModel);
return tmp;
}else {
// requestModel != null 加入队列
List<RequestModel> list = new ArrayList<RequestModel>();
list.add(requestModel);
map.put(key, list);
return key;
}
}
/**
* 请求资源
* @param key
* @param systemTime
* @return 是否在队头,是返回true,否返回false
*/
public static boolean getKey(String key, String sign) {
// 判断是否在队头
List<RequestModel> list = map.get(key);
RequestModel tmp = list.get(0);
if (tmp.getSign().equalsIgnoreCase(sign)) {
// 如果请求超过10秒才拿到资源就设置请求超时
int now = (int)(System.currentTimeMillis()/1000);
if (now - tmp.getSystemTime()/1000 > 55) {
tmp.setOverTime(true);
}
return true;
}
return false;
}
// 删除资源
public static void removeRequestModel(String key, String sign) {
List<RequestModel> list = map.get(key);
for (RequestModel requestModel : list) {
if (requestModel.getSign().equalsIgnoreCase(sign)) {
list.remove(requestModel);
break;
}
}
if (list.isEmpty()) {
map.remove(key);
}
}
/**
* 判断资源是否存在
* @param key "1,2,3,4"
* @return
*/
public static String containsKey(String key) {
if (null != map.keySet()) {
String[] keys = key.split(",");
Iterator<String> iterator = map.keySet().iterator();
while(iterator.hasNext()){
String tmp = iterator.next();
String[] tmps = tmp.split(",");
for (int i = 0; i < keys.length; i++) {
for (int j = 0; j < tmps.length; j++) {
if (keys[i].equalsIgnoreCase(tmps[j])) {
return tmp; // 找到就返回原来存入的key
}
}
}
}
}
return "0"; // 返回"0"表示没找到
}
}
3、每个用户购买支付时,调用接口算一个线程
String sign = UUID.randomUUID().toString();RequestModel requestModel = new RequestModel();
requestModel.setSign(sign);
requestModel.setOverTime(false);
requestModel.setSystemTime(System.currentTimeMillis());
// 加锁
SourceControl.reentrantLock.lock();
// 加入资源库
String key = SourceControl.putKey(tmp, requestModel);
boolean hasKey = true;
while(hasKey){
// 抢资源
hasKey = SourceControl.getKey(key, sign);
if (hasKey) {
hasKey = false;
}else {
hasKey = true;
SourceControl.condition.await(); // 继续等待
}
}
// 是否超时
if (requestModel.isOverTime()) {
//请求超时
}else {
// 这里处理事务,即获取云购码,当然,还有其他的事要做的,比如生成订单,处理库存之类
}
// 释放资源,让其他线程继续抢夺资源
SourceControl.removeRequestModel(key, sign);
SourceControl.condition.signalAll();
SourceControl.reentrantLock.unlock();
PS:synchronized我是配合着ReentrantLock和Condition这两个类来使用的
二、volatile,从这个单词的意思来说,易变的,不稳定的;(液体或油)易挥发的;爆炸性的;快活的,轻快的;
这个关键字我没有用过,在网上找了些资料,说是用volatile修饰的变量,我试了一下,比如下面的示例
public class Counter {
public volatile static int count = 0;
public static void add() {
//这里延迟1毫秒,使得结果明显
count++;
//这里每次运行的值都有可能不同,可能为1000
System.out.println("运行结果:Counter.count=" + Counter.count);
}
public static void main(String[] args) {
//同时启动1000个线程,去进行i++计算,看看实际结果
for (int i = 0; i < 1000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
Counter.add();
}
}).start();
}
}
}
输出的结果令人很不满意
运行结果:Counter.count=1
运行结果:Counter.count=4
运行结果:Counter.count=3
。。。
运行结果:Counter.count=892
运行结果:Counter.count=882
运行结果:Counter.count=881
我想说的是,用volatile 这个关键字,在多线程操作资源的时候,并没有起到作用,所以推荐使用synchronized配合着ReentrantLock和Condition来操作多线程处理资源问题。
- 学习java基础关键字之synchronized和volatile
- java多线程之synchronized和volatile关键字
- java 关键字synchronized和volatile
- Java线程入门学习5----volatile和synchronized关键字
- Java 多线程编程之synchronized 和 volatile关键字
- java线程5 volatile和synchronized关键字 .
- Java中的volatile和synchronized关键字
- synchronized和volatile关键字
- volatile和synchronized关键字
- synchronized和volatile关键字
- Volatile和Synchronized关键字
- java基础之volatile关键字
- Java基础之volatile关键字
- 谈谈java线程锁synchronized关键字和volatile关键字
- Java Volatile关键字和 Synchronized关键字的区别
- Java并发基础(四)-volatile和synchronized
- java学习之关键字volatile
- java之volatile关键字学习
- CentOS下MySQL 5.7编译安装
- 微信支付的坑
- 总结系列-LayoutInflater的inflate函数用法详解
- eclipse配置Tomcat服务器server locations的方法
- iOS APP 支持IPv6-only的注意事项及兼容性考虑
- 学习java基础关键字之synchronized和volatile
- 第十一届湖南省大学生程度设计竞赛部分题解
- 火星坐标转百度坐标
- Java并发编程中状态依赖性的管理
- linux系统编程之线程(二)
- 穷人做事、富人做市、商人做势[趋势]
- apple IOS的base64编解码
- 【幻化万千戏红尘】qianfengDay22-java基础学习:线程安全、synchronized、死锁
- nand flash_笔记