当前位置: 代码迷 >> J2SE >> 实则都不懂,多么痛的领悟-关于多线程的若干疑惑
  详细解决方案

实则都不懂,多么痛的领悟-关于多线程的若干疑惑

热度:79   发布时间:2016-04-23 20:09:18.0
其实都不懂,啊多么痛的领悟----关于多线程的若干疑惑
平时工作中很少用到多线程,理论上的东西懂一点,用起来才感觉问题巨多啊。求大神解惑!!
问题1:关于synchronized同步锁定的对象的问题
类似下面一段伪代码,对账户更新进行同步,使用第一种方式,感觉锁定是一个毫无意义的对象期,而第二种方式,锁定账户余额,但它是一个随时不断变化的,感觉很不科学?

public class ThreadTest {

public void updateAccount() {

Object lock = new Object();
String account = String.valueOf(num);
synchronized (lock) { // method 1
// TODO something
}
synchronized (account) { // method 2
// TODO something
}
}

}


问题2:
一个类实现Runnable接口,必须也只能实现一个run(),如果有多个业务逻辑需要多线程环境下运行,那只能拆分到多个类,分别实现Runnable接口?
问题3:synchronized同步的效率问题
偏向锁,轻量级锁?Concurrent?网上找了许多资料,越看越糊涂。所以引申出来第3个让我感到脸红的问题。

问题4:到底在什么情况下使用多线程?我不知道这个问题在是不是好笑,有人可能会说,这算啥问题,在并发量高,等待时间长,消耗资源大的时候就可以使用多线程。但是一些大型电商网站,查询并发也很高,在不考虑服务器压力的情况下(反正站在说话腰不疼的,嘿嘿),为什么不使用多线程而用缓存,貌似多线程肯定只是在对数据库更新操作的情况下使用。我大约只是在有数据需要同步的时候,才会思考需不需要用多线程,这算不算本末倒置。如果仅仅只是需要数据同步的话,那么直接只用户synchronized有无作用?如下:

public class test {

public void test(){
synchronized (this) {

}
}

}


考虑了一下,没有涉密和敏感的问题,直接贴上一段代码,没有优化,将就看,让板砖来得更猛烈些吧!!


public class ExchangeThread implements Runnable {

private static  String objectId = "";
private static String memberId ;
private static String num ;
private static String gold ;
private static String type ;

private static int respcode = 0;
private static String respmessage = "";

@SuppressWarnings("unchecked")
private static List<HashMap> list = null;

/**
 * 商品兑换
 * @param objectId 商品ID
 * @param memberId 会员ID
 * @param type 兑换方式 1:线下 2:邮寄
 */
public static String exchange(String objectId,String memberId,String num,String gold,String type) {
ExchangeThread.objectId = objectId;
ExchangeThread.memberId = memberId;
ExchangeThread.num = num;
ExchangeThread.gold = gold;
ExchangeThread.type = type;
ExchangeThread exchangeThread = new ExchangeThread();
exchangeThread.run();
return JsonUtil.getJsonStrObject(list.size()>0?list.get(0):null, respcode, respmessage);
}

public static String exchangeThread(String objectId,String memberId,String num,String gold,String type) {
respcode = Consts.debugdefine.loadDataErrorCode;
respmessage = "兑换失败!";
//使用当前系统时间,生成一个订单号,精确到秒,再加四位随机数,以确保唯一性
String orderno = OrderNoUtil.getOrderNo();
//计算总共所需金币
Double totalgold = Double.parseDouble(num)*Double.parseDouble(gold);
Connection conn = DBoperation.getConn();
String sql = " insert into exchange (orderno,product,member,num,golds,exchange_type,create_time) values(?,?,?,?,?,?,now())";
try {
int result = DBoperation.operateNoCommit(conn, new String[]{orderno,objectId,memberId,num,String.valueOf(totalgold),type}, sql);
if(result>0){
//会员金币数--totalgold
sql = "update member set gold=gold-? where id=?";
result = DBoperation.operateNoCommit(conn, new String[]{String.valueOf(totalgold),memberId}, sql);
if(result>0){
Object lock = new Object();
//得到兑换商品当前库存量,锁定
System.out.println("thread name:==================1"+Thread.currentThread().getName());
result = 0;
sql = "select stock from product where id=?";
list = DBoperation.queryForObjectList(DBoperation.getConn(), new String[]{objectId}, sql);
String stock = String.valueOf(list.get(0).get("stock"));

if(Integer.parseInt(stock)<Integer.parseInt(num)){
list.clear();
respmessage = "对不起,库存不足,暂时无法兑换!";
return null;
}
System.out.println("thread name:==================2"+Thread.currentThread().getName());
//synchronized (stock) {
synchronized (lock) {
//兑换商品库存量-num
Thread.sleep(1000*5);
sql = " update product set stock=stock-? where id=? ";
result = DBoperation.operateNoCommit(conn, new String[]{num.trim(),objectId}, sql);
}
if(result>0){
conn.commit();
respcode = Consts.debugdefine.loadDataSuccessCode;
respmessage = "兑换成功!";
//预订成功后,返回订单详情
sql = "select o.orderno,o.create_time,o.golds,o.num,p.name as pname,p.id as pid,o.status,p.price,address.name as aname," +
"address.address , address.telephone from exchange o left join product p on o.product=p.id left join member m " +
" on o.member=m.id left join (select a.id,a.telephone,a.member,a.name,a.address from address a where a.member=? and a.isDefault=1) " +
" as address on m.id=address.member  where o.orderno=?";
System.out.println("sql = "+sql);
list = DBoperation.queryForObjectList(DBoperation.getConn(), new String[]{memberId,orderno}, sql);
}
}
}
} catch (Exception e) {
DBoperation.rollback(conn);
e.printStackTrace();
}finally{
DBoperation.closeConn(conn);
}
return JsonUtil.getJsonStrObject(list.size()>0?list.get(0):null, respcode, respmessage);
}

@Override
public void run() {
exchangeThread(objectId, memberId, num, gold, type);
}

}

------解决思路----------------------
多线程可以用来处理一些并发(同时请求),异步操作(无需等待操作结果),而锁是为了避免多个线程同时访问临界资源造成数据的不一致,最后一个问题,使用了数据库 事务(数据库在修改数据的时候会锁定数据防止其他会话来修改),就没必要去使用锁了。
------解决思路----------------------
synchronized比可重入锁效率低
------解决思路----------------------
类似下面一段伪代码,对账户更新进行同步,使用第一种方式,感觉锁定是一个毫无意义的对象期,而第二种方式,锁定账户余额,但它是一个随时不断变化的,感觉很不科学?

加锁的目标是对象,和对象变不变无关

一个类实现Runnable接口,必须也只能实现一个run(),如果有多个业务逻辑需要多线程环境下运行,那只能拆分到多个类,分别实现Runnable接口?

是否需要多个Runnable取决于你业务的需求,如果你的业务里的步骤需要异步的并发,那么你是需要多个Runnable。但是我估计你想要的只是一个业务同时被调用,这种情况你不需要多个Runnable接口,只需要多个线程

问题3:synchronized同步的效率问题
偏向锁,轻量级锁?Concurrent?网上找了许多资料,越看越糊涂。所以引申出来第3个让我感到脸红的问题。
绝大部分的性能问题都不会是因为synchronized的效率问题引起的,反而,不正确的算法、加锁顺序、加锁策略,甚至数据库,网络等等才是性能问题的元凶,请不要在出了问题之后草率的下定论,去比较synchronized和重入锁哪个效率高,那是很初级的表现

问题4:到底在什么情况下使用多线程?我不知道这个问题在是不是好笑,有人可能会说,这算啥问题,在并发量高,等待时间长,消耗资源大的时候就可以使用多线程。但是一些大型电商网站,查询并发也很高,在不考虑服务器压力的情况下(反正站在说话腰不疼的,嘿嘿),为什么不使用多线程而用缓存,貌似多线程肯定只是在对数据库更新操作的情况下使用。我大约只是在有数据需要同步的时候,才会思考需不需要用多线程,这算不算本末倒置。如果仅仅只是需要数据同步的话,那么直接只用户synchronized有无作用?如下:
在存在有非CPU密集型阻塞的时候多线程可以大大的提高效率。因为多线程实际上消耗的是CPU的资源,有些操作例如网络、磁盘IO会阻塞住线程,这时CPU是空转的,效率被浪费了,所以才出现多线程的模型,就是要利用单线程阻塞期间CPU的工作时间。如果你的业务是CPU密集型,就不要上多线程了,线程间的切换会让你的性能雪上加霜。
电商网站的缓存和多线程完全不是一回事,解决的不是同一个问题。另外,java的web容器全部都是多线程的,只不过封装起来了你看不到,平时从来没有关注过,好像就是在写单线程一样。


代码什么的不想看了太长了






------解决思路----------------------
引用:
Quote: 引用:

 问题4:到底在什么情况下使用多线程?我不知道这个问题在是不是好笑,有人可能会说,这算啥问题,在并发量高,等待时间长,消耗资源大的时候就可以使用多线程。但是一些大型电商网站,查询并发也很高,在不考虑服务器压力的情况下(反正站在说话腰不疼的,嘿嘿),为什么不使用多线程而用缓存,貌似多线程肯定只是在对数据库更新操作的情况下使用。我大约只是在有数据需要同步的时候,才会思考需不需要用多线程,这算不算本末倒置。如果仅仅只是需要数据同步的话,那么直接只用户synchronized有无作用?如下:
在存在有非CPU密集型阻塞的时候多线程可以大大的提高效率。因为多线程实际上消耗的是CPU的资源,有些操作例如网络、磁盘IO会阻塞住线程,这时CPU是空转的,效率被浪费了,所以才出现多线程的模型,就是要利用单线程阻塞期间CPU的工作时间。如果你的业务是CPU密集型,就不要上多线程了,线程间的切换会让你的性能雪上加霜。
电商网站的缓存和多线程完全不是一回事,解决的不是同一个问题。另外,java的web容器全部都是多线程的,只不过封装起来了你看不到,平时从来没有关注过,好像就是在写单线程一样。


懂了,在非CPU密集型阻塞的时候,应该利用多线程把CPU充分利用起来,这个时候适用多线程。那么在仅需要数据同步的情况下,并不适用多线程,而是更适合选择数据库锁定?

缓存和多线程当然不是一回事儿,我只是打个不恰当的比方。我的本意是,在数据库查询操作的时候,抛开服务器压力暂且不说,如果CPU有空余,是否也可以使用多线程并发执行多个请求,只是我们有缓存这个更好更科学的选择?

“java的web容器全部都是多线程的”是否可以理解为,所有的请求都是可以并发执行的?


应用层也可以加锁 ,数据库也可以加锁,加锁粒度不一样,不可一概而论,这和多线程也不是一回事。

缓存和多线程当然不是一回事儿,我只是打个不恰当的比方。我的本意是,在数据库查询操作的时候,抛开服务器压力暂且不说,如果CPU有空余,是否也可以使用多线程并发执行多个请求,只是我们有缓存这个更好更科学的选择?
这根本不是个问题,因为你找不到非并发的服务器(Redis除外)。并发基本是服务器的通用解决方案。

“java的web容器全部都是多线程的”是否可以理解为,所有的请求都是可以并发执行的?
不是可以,是“就是”。你见到的所有web服务器都是多线程并发的,这个你可以稍微看一眼tomcat源码就很容易看出来
  相关解决方案