`
godblessalong
  • 浏览: 1350 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类

5分钟让你彻底了解JAVA多线程

阅读更多

前言

网上有多关于“JAVA多线程”的文章,但是对于不太熟悉多线程的编程人员来说人来说太抽象了,干脆我就直接用代码告诉大家何为多线程,不同的编写方式会带来怎样的后果以及如何正确的使用多线程。

示例程序功能描述

SynchronizedDemo.java业务功能:模拟火车售票场景。成员变量total表示火车表总量。showToal()一旦被执行就开始卖表(total-1),票没了(当total的值小于等于零)就停止卖票。

 

业务代码及执行结果

 

/**
 * 模拟火车售票场景。成员变量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 = 14
Thread-0 total = 13
Thread-0 total = 12
....
Thread-0 total = 11
Thread-0 total = 10
Thread-0 total = 9
Thread-0 total = 8
Thread-0 total = 7
Thread-0 total = 6
Thread-0 total = 5
Thread-0 total = 4
Thread-0 total = 3
Thread-0 total = 2
Thread-0 total = 1
Thread-0 total = 0
Thread-0耗时:0
Thread-7耗时:0
Thread-6耗时:0
Thread-5耗时:0
Thread-4耗时:0
Thread-3耗时:0
Thread-2耗时:0
Thread-1耗时:0
 没有出现负数的余票,我们的目标达到了。那一定会问为什么我在非static方法使用synchronized不生效呢?这就涉及到了线程同步锁的问题。

线程同步锁

如果要了解线程同步锁,你需要对JVM内存及对象初始化有基本了解,我们把上述三个场景通过图来描述(我们简称SynchronizedDemo为SD),将会更直观,

第一个场景Total出现负值,因为total静态成员变量,在内存中只有唯一的一份,他可以被多个线程中的showTotal操作,因此出现了并发操作的问题。如图,



 

第二个场景,我们寄希望于是用synchronized解决上述问题,但是由于showTotal()非static,因此及时使用了synchronized,也是各自对象加各自的锁。达不到控制并发的目的。如图,



 

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



 

通过“线程同步锁”的讲解,我们发现控制并发是有先决条件的,

1、在同一个JVM内;

2、操作同一资源的线程,其线程锁要一致;

使用多线程需要注意的事项

实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

我们思考下,是否可以使用synchronized和static双重约束的方法控制数据库并发呢? 

答案是否定的,通过三个场景在JVM中的剖析图,我们可以清楚的发现,synchronized和static双重约束的方法的上下文为单个JVM,超过了这个范围他就力不从心了。所以如果要控制数据库的并发,还要使用数据库的技术,正所谓术业有专攻。

 

许井龙 于腊月二十四

 

 

 

  • 描述: 同步锁1
  • 大小: 20 KB
  • 大小: 23.9 KB
  • 大小: 18.3 KB
分享到:
评论
2 楼 wawxy2009 2013-03-27  
我来顶了,呵呵 
1 楼 caijingbin0207 2013-02-19  
不错

相关推荐

Global site tag (gtag.js) - Google Analytics