`
godblessalong
  • 浏览: 1433 次
  • 性别: 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  
不错

相关推荐

    java多线程教程——一个课件彻底搞清多线程

    Java多线程是Java编程中的核心概念,它允许程序同时执行多个任务,从而提高应用程序的效率和响应性。本教程将深入讲解Java线程的相关知识,包括进程与线程的基本概念、线程的创建和启动、多线程的互斥与同步、线程...

    Java Swing多线程死锁问题解析

    在解决Java Swing多线程死锁问题时,我们需要了解Java Swing的多线程机理和相关API的使用。SwingUtilities.invokeLater和SwingUtilities.isEventDispatchThread()是我们解决死锁问题的重要工具。 在实际开发中,...

    java线程同步实例

    哲学家就餐问题是多线程编程中一个经典的同步问题。该问题描述为五位哲学家围坐在一张圆桌旁,桌上放着五个筷子(或者说是餐叉),每个哲学家在思考的时候不使用筷子,而当他们想吃饭时,则需要同时拿到左右两边的...

    【2018最新最详细】并发多线程教程

    【2018最新最详细】并发多线程教程,课程结构如下 1.并发编程的优缺点 2.线程的状态转换以及基本操作 3.java内存模型以及happens-before规则 4.彻底理解synchronized 5.彻底理解volatile 6.你以为你真的了解final吗...

    java-多线程下载器(支持断点续传、线程加减)包含源码和可运行jar包 第二版

    2、支持多任务多线程同时下载; 3、每个任务的线程数由用户在新建任务时自定义,缺省为5个线程; 4、任务下载过程中可以点击“线程+”或“线程-”即时增减线程; 5、选择任务,可以在任务信息栏中查看任务下载的信息...

    彻底研究Java2源码

    5. **多线程编程**: Java2引入了丰富的多线程支持,包括Thread类、synchronized关键字、wait/notify机制等。源码分析可以帮助理解线程同步和通信,避免并发问题。 6. **输入/输出与网络编程**: Java2提供了强大...

    407.405.JAVA基础教程_IDEA的使用与多线程-IDEA的卸载(407).rar

    本教程聚焦于JAVA基础和IDEA的使用,同时也涵盖了多线程的概念以及IDEA的卸载过程。 首先,让我们来探讨JAVA基础。Java是一门面向对象的编程语言,以其“一次编写,到处运行”的特性而闻名。它的核心概念包括类、...

    彻底明白Java的IO系统

    首先,我们需要了解Java中的流。在Java中,I/O操作是通过流来完成的,流可以看作数据传输的通道。流分为两种类型:输入流(InputStream)用于读取数据,输出流(OutputStream)用于写入数据。根据处理的数据类型,流...

    第20章 Part1 多线程介绍.pdf

    本章主要介绍多线程编程的基础知识,包括并发与多线程的概念、多线程编程的重要性、线程的运行机制、并发编程面临的挑战以及如何使用Java中的线程进行编程。 现代计算机具有多个核心,通常为5-10个,但也有如Intel...

    java 图片管理系统代码及设计.ppt

    Java 是一种简单的、跨平台的、面向对象的、分布式的、解释的、健壮的安全的、结构的中立的、可移植的、性能很优异的多线程的、动态的语言。Java 语言的出现彻底改变了互联网上的信息内容,让我们的互联网丰富多彩...

    彻底明白 Java 语言中的IO系统

    ### 彻底理解Java语言中的IO系统 #### 一、Input和Output Java IO系统是Java编程中的一个重要组成部分,它负责处理程序与外部资源(如文件、网络等)之间的数据交换。Java IO的设计以流为基础,可以分为输入流...

    Java API 1.8英文版

    6. **多线程**:Java API提供了强大的多线程支持,包括`java.lang.Thread`类和`java.util.concurrent`包中的并发工具类,如`ExecutorService`和`Semaphore`。 7. **Lambda表达式和函数式编程**:Java 8引入了lambda...

    Java8学习全套PPT

    4. **日期和时间API**:Java8彻底改变了日期和时间的处理,引入了`java.time`包,提供了更直观、更易用的API,如`LocalDate`、`LocalTime`、`LocalDateTime`等,替代了原来的`java.util.Date`和`Calendar`。...

    JAVA白皮书(英文版)

    - **多线程支持总结**:总结Java多线程支持的优点和应用场景。 #### 8. 性能和比较 ##### 8.1 性能 - **性能因素**:讨论影响Java程序性能的因素及其优化策略。 ##### 8.2 Java语言的比较 - **与其他语言的比较**...

    cms-java-源码

    2. **JVM内存模型**:了解Java内存分配和回收的过程,以及老年代在整个内存管理中的角色。 3. **并发编程**:理解CMS如何在多线程环境下保证垃圾收集的正确性。 4. **性能优化**:学习如何调整CMS参数以达到最佳的...

    Java内存模型的历史变迁.docx

    Java内存模型是Java多线程编程中的核心概念,它规定了如何处理线程间的共享变量访问,以确保数据的一致性和正确性。本文主要探讨了Java内存模型从早期到JDK5之后的历史变迁。 在旧的Java内存模型中,线程通过共享...

    Mastering Concurrency Programming with Java 8

    - **原子操作与变量**:原子操作是指一个不可中断的操作,即使在多线程环境下也能保证完整性。Java 8通过`java.util.concurrent.atomic`包提供了一组原子变量类,如`AtomicInteger`。 - **共享内存与消息传递**:...

    java爬虫项目实战源码.rar

    4. **多线程与并发**:为了提高爬虫的效率,通常会采用多线程或异步处理。Java的并发库提供了丰富的工具,如ExecutorService、Future、Callable等,用于管理和协调多个任务。 5. **爬虫框架**:如Jsoup+CrawlingKit...

    【Java分享客栈】一文搞定京东零售开源的AsyncTool,彻底解决异步编排问题。.doc

    在上一篇讲CompletableFuture的文章中,我们了解了CompletableFuture提供的串行、并行等常用异步编排的方案。但是,在细节上还是有许多不足,比如回调方面,编排复杂顺序方面。因此,我们需要一种更加强大且灵活的...

Global site tag (gtag.js) - Google Analytics