2022年什么是同步,如何在多线程间保持同步参照 .pdf
《2022年什么是同步,如何在多线程间保持同步参照 .pdf》由会员分享,可在线阅读,更多相关《2022年什么是同步,如何在多线程间保持同步参照 .pdf(12页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、4.5 什么是同步,如何在多线程间保持同步http:/ 2007-04-07 13:31 徐彬清华大学出版社我要评论(1)摘要:XJava 程序设计专家门诊正文部分的第5 节“什么是同步,如何在多线程间保持同步”。这一节学习如何解决线程安全问题,引入同步机制,实现在多线程访问同一资源的时候保持同步。同时,附有专家说明和专家指点,供大家参考!标签:Java 线程同步 Java 专家 XJava 程序设计专家门诊限时报名参加“甲骨文全球大会 2010 北京”及“JavaOne 和甲骨文开发者大会2010”问题在前面的小节中,所涉及的线程大多都是独立的,而且异步执行。也就是说每个线程都包含了运行时自
2、身所需要的数据或方法,而不需要外部的资源或方法,也不必关心其他线程的状态或行为。但是,有时候在进行多线程的程序设计中需要实现多个线程共享同一段代码,从而实现共享同一个私有成员或类的静态成员的目的。这时,由于线程和线程之间互相竞争CPU 资源,使得线程无序地访问这些共享资源,最终可能导致无法得到正确的结果。例如,一个多线程的火车票预订程序中将已经预订过的火车票再次售出,这是由于当该车票被预订以后没有及时更新数据库中的信息而导致在同一时刻购买该火车票的另一乘客也将其预订。这一问题通常称为线程安全问题,为了解决这个问题,必须要引入同步机制,那么什么是同步,如何实现在多线程访问同一资源的时候保持同步呢
3、?解决思路首先分析一个多线程的程序,在这个程序中各线程之间共享同一数据资源,从而模拟出火车站订票系统的处理程序,但是最后程序却出现了意料不到的结果。这个火车站订票系统不同步的模拟程序代码如下。/例 4.5.1 NoSynchronizeDemo.java class SaleTickets implements Runnable private String ticketNo=100750;/车票编号private int ticket=1;/共享私有成员,编号为100750的车票数量为 1 public void run()System.out.println(Thread.currentT
4、hread().getName()+is saling Ticket+ticketNo);/当前系统正在处理订票业务if(ticket0)try /休眠 0-1000毫秒,用来模拟网络延迟名师资料总结-精品资料欢迎下载-名师精心整理-第 1 页,共 12 页 -Thread.sleep(int)(Math.random()*1000);catch(InterruptedException e)e.printStackTrace();ticket=ticket-1;/修改车票数据库的信息/显示当前该车票的预订情况System.out.println(tickets amount is left:
5、+ticket);else/显示该车票已被预订System.out.println(Sorry,Ticket+ticketNo+is saled);class NoSynchronizeDemo /创建两个线程模拟两个订票系统的订票过程 public static void main(String args)SaleTickets m=new SaleTickets();Thread t1=new Thread(m,System 1);Thread t2=new Thread(m,System 2);t1.start();t2.start();程序的要求是创建两个线程模拟两个预订票子系统,这两
6、个订票子系统将共享同一个数据库信息,只不过在这里数据库中只有一张编号为100750 的车票。当该车票在其中一个系统中被预定以后,则不能再被其他订票系统预订,并且能够提示抱歉信息。运行这个程序,观察输出的结果,如图4.5.1 所示:名师资料总结-精品资料欢迎下载-名师精心整理-第 2 页,共 12 页 -图 4.5.1 模拟两个订票系统的订票过程可以发现同一时间有两个订票系统在处理编号为100750 的车票的预订请求,结果由于网络延迟的影响,该车票被预订了两次,使得车票的数量变成了负数。出现这一严重问题的原因就是因为两个线程同时进入了共享代码区域。这个共享代码区域就是程序中的if else语句的
7、内容。当线程 t1 执行到 if(ticket0)时,立即休眠若干毫秒,而此时线程t2 得到执行,也执行到 if(ticket0),此时线程t1 休眠结束继续执行下面的代码。经过两个线程的交替执行,最终完成预订服务,结果就出现了车票被预订了两次的严重错误。程序中之所以在run()方法中使用sleep()方法,是因为在实际联网的预订票系统中确实存在着因为某些原因使得预定过程暂时中止的情况,而这一情况也极有可能发生在执行了if(ticket0)语句之后,修改数据库信息之前。那么,如何避免这种情况发生呢?实际上在预订票系统中的这种暂时中止,瞬时延迟的情况是很普遍的。因此只能对程序做一些处理,使得一个
8、预订票操作完全结束以后才能处理下一个预订票操作,以此来保证数据库的一致性和操作的正确性。Java提供了这样的方法,只需要在程序中引入同步机制就可以完全解决这类线程安全问题。什么是同步呢?当两个或多个线程需要访问同一资源时,它们需要以某种顺序来确保该资源某一时刻只能被一个线程使用的方式称为同步。要想实现同步操作,必须要获得每一个线程对象的锁。获得它可以保证在同一时刻只有一个线程访问对象中的共享关键代码,并且在这个锁被释放之前,其他线程就不能再进入这个共享代码。此时,如果还有其他线程想要获得该对象的锁,只得进入等待队列等待。只有当拥有该对象锁的线程退出共享代码时,锁被释放,等待队列中第一个线程才能
9、获得该锁,从而进入共享代码区。Java在同步机制中提供了语言级的支持,可以通过对关键代码段使用synchronized 关键字修饰来实现针对该代码段的同步操作。实现同步的方式有两种,一种是利用同步代码块来名师资料总结-精品资料欢迎下载-名师精心整理-第 3 页,共 12 页 -实现同步,一种是利用同步方法来实现同步。下面将分别介绍这两种方法,并给出实际的例子。具体步骤(1)使用同步代码块为了防止多个线程无序地访问共享资源,只需将对共享资源操作的关键代码放入一个同步代码块中即可。同步代码块的语法形式如下所示:synchronized(Object)/关键代码 其中,Object 是需要同步的对象
10、的引用。当一个线程欲进入该对象的关键代码时,JVM将检查该对象的锁是否被其他线程获得,如果没有,则JVM 把该对象的锁交给当前请求锁的线程,该线程获得锁后就可以进入花括弧之间的关键代码区域。对例 4.5.1 的程序进行修改,得到如下代码。/例 4.5.2 SynchronizeDemo.java class SaleTickets implements Runnable private String ticketNo=100750;/车票编号private int ticket=1;/共享私有成员,编号为100750的车票数量为 1 public void run()System.out.pr
11、intln(Thread.currentThread().getName()+is saling Ticket+ticketNo);/当前系统正在处理订票业务/下面同步代码块中的代码为关键代码,用synchronized关键字来标识synchronized(this)if(ticket0)try /休眠 0-1000毫秒,用来模拟网络延迟Thread.sleep(int)(Math.random()*1000);catch(InterruptedException e)ticket=ticket-1;/修改车票数据库的信息名师资料总结-精品资料欢迎下载-名师精心整理-第 4 页,共 12 页
12、-System.out.println(ticket is saled by +Thread.currentThread().getName()+,amount is:+ticket);/显示当前该车票的预订情况 else System.out.println(Sorry+Thread.currentThread().getName()+,Ticket+ticketNo+is saled);/显示该车票已被预订 class SynchronizeDemo public static void main(String args)SaleTickets m=new SaleTickets();Th
13、read t1=new Thread(m,System 1);Thread t2=new Thread(m,System 2);t1.start();t2.start();本程序中,线程t1 先获得该关键代码的对象的锁,因此,当线程t2 也开始执行并欲获得关键代码的对象的锁时,发现该锁已被线程t1 获得,只好进行等待。当线程t1 执行完关键代码后,会将锁释放并通知线程t2,此时线程t2 才获得锁并开始执行关键代码。运行这个程序,将看到如图4.5.2 所示的结果。图 4.5.2 使用了同步代码块可以看到几乎在同一时间两个系统都获得了预订票的指令,但是由于预订票子系统System1 比 Syste
14、m2 先得到处理,因此编号为100750 的车票就必须先售给在System1 提交名师资料总结-精品资料欢迎下载-名师精心整理-第 5 页,共 12 页 -预定请求的乘客。而在System2 提交预定请求的乘客并没有得到编号为100750 的车票,因此系统提示抱歉信息。这一切正确地显示都源于引入了同步机制,将程序中的关键代码放到了同步代码块中,才使得同一时刻只能有一个线程访问该关键代码块。可见,同步代码块的引入保持了关键代码的原子性,保证了数据访问的安全。程序中用到了this 来作为同步的参数,这种方式会将整个对象都上锁,因为 this 代表了当前线程对象。因此同一时刻只能有一个线程访问共享资
15、源。不过,也可以使用虚拟对象来上锁:class SaleTickets implements Runnable String str=;/创建一个空字符串来作为虚拟对象上锁public void run()synchronized(str)/同步代码块中的代码为关键代码 当需要判断关键代码的锁是否被某一线程所获取时,可以使用Thread 类的静态布尔型方法 holdsLock(Object o)来进行测试,其中参数o 是与判断的关键代码锁所对应的对象的引用。如果某一线程已进入同步代码块或者同步方法,正在访问该对象的关键代码段,那么holdsLock()方法将返回一个布尔真值,否则,返回一个布尔
16、假值。(2)使用同步方法同步方法和同步代码块的功能是一样的,都是利用互斥锁实现关键代码的同步访问。只不过在这里通常关键代码就是一个方法的方法体,此时只需要调用synchronized 关键字修饰该方法即可。一旦被 synchronized 关键字修饰的方法已被一个线程调用,那么所有其他试图调用同一实例中的该方法的线程都必须等待,直到该方法被调用结束后释放其锁给下一个等待的线程。将例4.5.2 的程序作一些改动得到下面的代码。/例 4.5.3 SynchronizeDemo2.java class SaleTickets implements Runnable private String ti
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 2022年什么是同步 如何在多线程间保持同步参照 2022 什么是 同步 如何 多线程 保持 参照
限制150内