在慕课网上看过多线程的课程。
当某一个线程访问到某个类的含有synchronized标志的方法、synchronized的代码块的时候,获得了当前对象的锁,这个时候,其他线程不能访问该对象的这个synchronized方法。比如(慕课网课程上的)能量系统,初始化了一个能量系统,然后有一百个线程去不断获取锁、释放锁实现了能量的转移,一个线程在访问这个能量转移方法的时候,获取了这个EnergySystem的实例对象的锁,其他的线程的就不能操作了。
还有一个减少资源损耗的操作是:当能量不够的时候,为了避免重复申请锁,lockObj.wai()方法调用了,然后进入了waitset等其他线程的lockObj.notifyAll()的调用。
其实也可以在EnergySystem里面进行this的锁,内置对象锁,锁住的是能量系统的实例energySystem,这样很多个转换任务EnergyTransferTask的线程去进行对象内部数组之间能量转换的时候,需要获取的是energySystem的对象锁,然后进行操作,这样操作完成之后才释放锁。
在EnergySystem中锁对象可以这样构建
//构建锁对象 private final Object lockObj = new Object();
然后同步的代码块 在EnergySystem中:
synchronized (lockObj){ //避免持续申请锁 while (energyBoxes[from] < amount) { try { lockObj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //wait方法调用之后 当前线程进入锁对象上的等待集合中waitset System.out.println(Thread.currentThread().getName()); energyBoxes[from] -= amount; System.out.printf("从%d转移%10.2f单位能量到%d",from,amount,to); energyBoxes[to] += amount; System.out.printf("能量总和: %10.2f %n",getTotalEnergies()); lockObj.notifyAll(); //告诉所有等待的线程 不用等待了 }
注意点
1、此时每个线程都是通过lockObj访问同步代码块,总共只有1把对象锁,单个线程获取了之后其他线程都不能获取这个锁了。所以要等待运行,如果能量不够,还要去锁对象上的waitset等待。
2、如果一个线程访问了这个synchronized方法,那么其他所有的synchronized方法也不能被其他线程访问了。
-------------------------------------------------分隔线
在某次讨论中说到了用户购买物品,背景是可以两个地方同时登陆,为了抢一件货物,同时点击购买产生了两次重复购买,都成立,导致了最后账户为负数。
一种解决方案是在sql中加入判断账户余额大于零的语句
一种解决方案是在service层购买货物的地方,通过session集合获取当前用户,然后
synchronized(user){ //执行购买 }这样用户的两个session对应的两个购买操作应该也是一个user,他们的操作也是先后进行的。