5分钟让你彻底了解JAVA多线程
/** * 模拟火车售票场景。成员变量total表示火车表总量。showToal()一旦被执行就开始卖表(total-1),票没了(当total的值小于等于零)就停止卖票 * @author 许井龙 * */public class SynchronizedDemo implements Runnable {/* 火车票总量*/private static int total = 100;/** * 程序入口 * @param args */public static void main(String[] args) {//模拟八个售票窗口 Thread td1 = new Thread(new SynchronizedDemo()); Thread td2 = new Thread(new SynchronizedDemo()); Thread td3 = new Thread(new SynchronizedDemo()); Thread td4 = new Thread(new SynchronizedDemo()); Thread td5 = new Thread(new SynchronizedDemo()); Thread td6 = new Thread(new SynchronizedDemo()); Thread td7 = new Thread(new SynchronizedDemo()); Thread td8 = new Thread(new SynchronizedDemo()); //八个售票窗口同时售票; td1.start(); td2.start(); td3.start(); td4.start(); td5.start(); td6.start(); td7.start(); td8.start();}@Override/** * 多线程执行入口 */public void run() {//售票:稍后会有不同的具体实现showToal();}}?如果showToal()代码如下,
?
?
public void showToal(){//long sTime = System.currentTimeMillis();while(true){//当总量(total)当小于等于0的时候,就不能在继续减少了if(total>0){ try { //模拟线程阻塞1ms Thread.sleep(1); } catch (Exception e) { e.printStackTrace(); } total = total - 1; System.out.println(Thread.currentThread().getName() + " 余票: " + total + "张"); }else{//反之结束程序break;}}//System.out.println(Thread.currentThread().getName() + "耗时:" + (System.currentTimeMillis() - sTime)/1000);}?执行结果如下,?
?
....Thread-4 余票: 5张Thread-1 余票: 4张Thread-0 余票: 3张Thread-3 余票: 0张Thread-5 余票: 1张Thread-2 余票: 2张Thread-7 余票: -1张Thread-1 余票: -2张Thread-6 余票: -3张Thread-4 余票: -4张?余票居然出现了负数,显然是我们程序出现了漏洞。聪明的你马上就意识到,showTota()没有使用synchronized。因此你修改了showTota(),
?
?
public synchronized void showToal(){//long sTime = System.currentTimeMillis();while(true){// 当总量(total)当小于等于0的时候,就不能在继续减少了if(total>0){ try { //模拟线程阻塞1ms Thread.sleep(1); } catch (Exception e) { e.printStackTrace(); } total = total - 1; System.out.println(Thread.currentThread().getName() + " 余票: " + total + "张"); //反之结束程序}else{break;}}//System.out.println(Thread.currentThread().getName() + "耗时:" + (System.currentTimeMillis() - sTime)/1000);}??但是我们看看运行结果,?
?
Thread-5 余票: 4张Thread-2 余票: 4张Thread-4 余票: 6张Thread-6 余票: 6张Thread-3 余票: 6张Thread-0 余票: 2张Thread-7 余票: 2张Thread-4 余票: -3张Thread-1 余票: -4张Thread-2 余票: -4张Thread-3 余票: -3张Thread-5 余票: -3张Thread-6 余票: -3张Thread-0 余票: -6张Thread-7 余票: -6张?似乎和你预期的不太一样哦。那么是synchronized没生效?还是我们使用的不对呢?现在我告诉你:showToal()需要声明为static。public synchronized static void showToal(){//long sTime = System.currentTimeMillis();while(true){if(total>0){ try { Thread.sleep(1); } catch (Exception e) { e.printStackTrace(); } total = total - 1; System.out.println(Thread.currentThread().getName() + " 余票: " + total + "张"); }else{break;}}//System.out.println(Thread.currentThread().getName() + "耗时:" + (System.currentTimeMillis() - sTime)/1000);}?我们再来看看执行结果,
?
?
们再来看看执行结果,Thread-0 total = 14Thread-0 total = 13Thread-0 total = 12....Thread-0 total = 11Thread-0 total = 10Thread-0 total = 9Thread-0 total = 8Thread-0 total = 7Thread-0 total = 6Thread-0 total = 5Thread-0 total = 4Thread-0 total = 3Thread-0 total = 2Thread-0 total = 1Thread-0 total = 0Thread-0耗时:0Thread-7耗时:0Thread-6耗时:0Thread-5耗时:0Thread-4耗时:0Thread-3耗时:0Thread-2耗时:0Thread-1耗时:0?没有出现负数的余票,我们的目标达到了。那一定会问为什么我在非static方法使用synchronized不生效呢?这就涉及到了线程同步锁的问题。
?
第二个场景,我们寄希望于是用synchronized解决上述问题,但是由于showTotal()非static,因此及时使用了synchronized,也是各自对象加各自的锁。达不到控制并发的目的。如图,

?
第三个场景,通过synchronized和static双重约束,我们发现total不再出现负数,因为showTotal被声明为static后,内存中也只有一份,因此其线程锁也是唯一的,这样就很好的控制了线程并发。

?
通过“线程同步锁”的讲解,我们发现控制并发是有先决条件的,
1、在同一个JVM内;
2、操作同一资源的线程,其线程锁要一致;
使用多线程需要注意的事项实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
我们思考下,是否可以使用synchronized和static双重约束的方法控制数据库并发呢??
答案是否定的,通过三个场景在JVM中的剖析图,我们可以清楚的发现,synchronized和static双重约束的方法的上下文为单个JVM,超过了这个范围他就力不从心了。所以如果要控制数据库的并发,还要使用数据库的技术,正所谓术业有专攻。
?
许井龙 于腊月二十四
?