




已阅读5页,还剩17页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
资源下载地址:/detail/cloudyxuq/3763101四多线程的同步以一个取钱列子来分析:(用户登录那些省略)Accout类:java view plaincopy1 /*银行取钱,账户类*/ 2 public class Accout 3 /账户编号 4 private String accoutNo; 5 /账户余额 6 private double balance; 7 /账户名称 8 private String accoutName; 9 public Accout() 10 super(); 11 12 public Accout(String accoutNo,String accoutName, double balance) 13 super(); 14 this.accoutNo = accoutNo; 15 this.balance = balance; 16 this.accoutName=accoutName; 17 18 public String getAccoutNo() 19 return accoutNo; 20 21 public void setAccoutNo(String accoutNo) 22 this.accoutNo = accoutNo; 23 24 public double getBalance() 25 return balance; 26 27 public void setBalance(double balance) 28 this.balance = balance; 29 30 public String getAccoutName() 31 return accoutName; 32 33 public void setAccoutName(String accoutName) 34 this.accoutName = accoutName; 35 36 /根据accoutNohe来计算Accout的hashcode和判断equals 37 Override 38 public int hashCode() 39 return accoutNo.hashCode(); 40 41 Override 42 public boolean equals(Object obj) 43 if(obj!=null&obj.getClass()=Accout.class) 44 Accout target=(Accout)obj; 45 return target.getAccoutNo().equals(accoutNo); 46 47 return false; 48 49 DrawThread类:java view plaincopy50 /*取钱的线程类*/ 51 public class DrawThread implements Runnable 52 /模拟用户账户 53 private Accout accout; 54 /当前取钱线程所希望取得值 55 private double drawAmount; 56 public DrawThread(Accout accout, double drawAmount) 57 super(); 58 this.accout = accout; 59 this.drawAmount = drawAmount; 60 61 /如果多个线程修改同一个共享数据时,会发生数据安全问题 62 public void run() 63 /账户余额大于取款金额时 64 if(accout.getBalance()=drawAmount) 65 /取款成功 66 System.out.println(Thread.currentThread().getName()+accout.getAccoutName()+取款成功:吐出钞票:+drawAmount); 67 /修改余额 68 accout.setBalance(accout.getBalance()-drawAmount); 69 System.out.println(当前余额为:+accout.getBalance(); 70 71 /账户金额不够时 72 else 73 System.out.println(账户金额不够,您的余额只有+accout.getBalance(); 74 75 76 TestDraw测试类:java view plaincopy77 public class TestDraw 78 public static void main(Stringargs) throws InterruptedException 79 /创建一个用户 80 Accout acct=new Accout(123456, 小明, 1000); 81 /模拟四个线程同时操作 82 DrawThread dt=new DrawThread(acct,600); 83 /DrawThread dt1=new DrawThread(acct,800); 84 Thread th1=new Thread(dt,线程1); 85 Thread th2=new Thread(dt,线程2); 86 Thread th3=new Thread(dt,线程3); 87 Thread th4=new Thread(dt,线程4); 88 th4.join(); 89 th1.start(); 90 th2.start(); 91 th3.start(); 92 th4.start(); 93 94 1.同步代码块Java多线程支持引入了同步监视器来解决多线程安全,同步监视器的常用方法就是同步代码块:java view plaincopy95 Synchronized(obj) 96 /.同步代码块 97 括号中的obj就是同步监视器:上面的语句表示:线程开始执行同步代码块之前,必须先获得对同步监视器的锁定。这就意味着任何时刻只能有一条线程可以获得对同步监视器的锁定,当同步代码块执行结束后,该线程自然释放了对该同步监视器的锁定。虽然java中对同步监视器使用的对象没有任何要求,但根据同步监视器的目的:阻止两条线程对同一个共享资源进行并发访问。所以一般将可能被并发访问的共享资源充当同步监视器。修改后如下:java view plaincopy98 public void run() 99 synchronized (accout) 100 /账户余额大于取款金额时 101 if(accout.getBalance()=drawAmount) 102 /取款成功 103 System.out.println(Thread.currentThread().getName()+accout.getAccoutName()+取款成功:吐出钞票:+drawAmount); 104 /修改余额 105 accout.setBalance(accout.getBalance()-drawAmount); 106 System.out.println(当前余额为:+accout.getBalance(); 107 108 /账户金额不够时 109 else 110 System.out.println(账户金额不够,您的余额只有+accout.getBalance(); 111 112 113 2.同步方法(synchronized可以修饰方法,代码块。不能修饰属性和构造方法)除了同步代码块外还可以使用synchronized关键字来修饰方法,那么这个修饰过的方法称为同步方法。对于同步方法来说,无需显式指定同步监视器,同步方法的同步监视器是this,也就是该对象本身,也就是上面TestDraw中定义的Accout类型的acct。java view plaincopy114 public void run() 115 draw(); 116 117 public synchronized void draw() 118 if(accout.getBalance()=drawAmount) 119 /取款成功 120 System.out.println(Thread.currentThread().getName()+accout.getAccoutName()+取款成功:吐出钞票:+drawAmount); 121 /修改余额 122 accout.setBalance(accout.getBalance()-drawAmount); 123 System.out.println(当前余额为:+accout.getBalance(); 124 125 /账户金额不够时 126 else 127 System.out.println(账户金额不够,您的余额只有+accout.getBalance(); 128 129 这里最好是将draw()方法写到Accout中,而不是像之前将取钱内容保存在run方法中,这种做法才更符合面向对象规则中的DDD(Domain Driven Design领域驱动设计)对于可变类的同步会降低程序运行效率。不要对线程安全类德所有方法进行同步,只对那些会改变共享资源的方法同步。单线程环境(可以使用线程不安全版本保证性能) 多线程环境(线程安全版本)2.1同步监视器的锁定什么时候释放 A.当前线程的同步方法,同步块执行结束。当前线程释放同步监视器 B.在同步方法,块中遇到break,return终止了该代码块,方法.释放 C.在代码块,方法中出现Error,Exception D.执行同步时,程序执行了同步监视器对象的wait()方法,则当前线程暂停,释放2.2同步监视器的锁定在以下情况不会被释放A.执行同步时,程序调用Thread.sleep(),Thread.yield()方法来暂停当前线程的执行,不会释放B.执行同步时,其他线程调用了该线程的suspend方法将该线程挂起,不会释放(但是尽量避免使用suspend和resume来控制线程,容易导致死锁)3.同步锁(Lock) 在jdk1.5后,java除了上面两种同步代码块和同步方法之外,还提供了一种线程同步机制:它通过显示定义同步锁对象来实现同步,在这种机制下,同步锁应该使用Lock对象充当。Lock是控制多个线程对共享资源进行访问的工具,每次只能有一个线程对Lock对象枷锁,线程开始访问共享资源之前应先获得Lock对象。(特例:ReadWriteLock锁可能允许对共享资源并发访问)。在实现线程安全控制中,通常喜欢使用可重用锁(ReentrantLock),使用该Lock对象可以显示的加锁,释放锁。CODE:java view plaincopy130 /声明锁对象 131 private final ReentrantLock relock=new ReentrantLock(); 132 public void run() 133 draw(); 134 135 public void draw() 136 /加锁 137 relock.lock(); 138 try 139 /账户余额大于取款金额时 140 if(accout.getBalance()=drawAmount) 141 /取款成功 142 System.out.println(Thread.currentThread().getName()+accout.getAccoutName()+取款成功:吐出钞票:+drawAmount); 143 /修改余额 144 accout.setBalance(accout.getBalance()-drawAmount); 145 System.out.println(当前余额为:+accout.getBalance(); 146 147 /账户金额不够时 148 else 149 System.out.println(账户金额不够,您的余额只有+accout.getBalance(); 150 151 152 /释放锁 153 finally 154 relock.unlock(); 155 总结: 同步方法和同步代码块使用与共享资源相关的,隐式的同步监视器,并且强制要求加锁和释放锁要出现在一个块结构中,而且当获取了多个锁时,它们必须以相反的顺序释放,且必须在与所有锁被获取时相同的范围内释放所有锁。虽然同步方法,代码块的范围机制使多线程安全编程非常方便,还可以避免很多涉及锁的常见编程错误,但有时也需要以更灵活的方式使用锁。Lock提供了同步方法,代码块中没有的其他功能(用于非块结构的tryLock方法,获取可中断锁lockInterruptibly方法,获取超时失效锁的tryLock(long,TimeUnit)方法)。ReentrantLock锁具有重入性,即线程可以对它已经加锁的ReentrantLock锁再次加锁,ReentrantLock对象会维持一个计数器来追踪lock方法的嵌套调用,线程每次调用lock()加锁后,必须显示的调用unlock()释放锁,所以一段被锁保护的代码可以调用另一个被相同锁保护的方法。4.死锁当两个线程相互等待对方释放同步监视器的时候就会发生死锁,一旦出现死锁,整个程序既不会发生任何异常,也不会有任何提示,只是所有线程处于阻塞状态,无法继续。五线程通信线程在系统内运行时,线程的调度具有一定的透明性,程序通常无法准确控制线程的轮换执行,可以通过以下方法来保证线程协调运行.1.线程协调运行如果对于一些方法是用同步方法或者同步代码块,那么可以调用Object类提供的wait(),notify(),notifyAll()。这三个不属于Thread,属于Object类,但必须由同步监视器来调用(同步方法的监视器是this:则需this.wait().,同步代码块的监视器是括号中的obj.wait();2.使用条件变量来控制协调如果程序没有使用sychronized来保证同步,可以使用Lock来保证同步,则系统中就不存在隐式的同步监视器对象,也就不能使用wait,notify,notifyAll来协调了。java view plaincopy156 private final Lock lock=new ReentrantLock(); 157 private final Condition cond=lock.newCondition(); 通过上面两行代码,条件Condition实际是绑定在一个Lock对象上的。相对应的Condition类也有三个方法: await(),signal(),signalAll()Account账号类:代码:java view plaincopy158 /* 账户类,用面向对象的DDD设计模式来设计 */ 159 /* 160 * DDD领域驱动模式,即将每个类都认为是一个完备的领域对象, 例如Account代表账户类,那么就应该提供用户账户的相关方法(存,取,转),而不是将 161 * setXXX方法暴露出来任人操作。 只要设计到DDD就需要重写equals和hashcode来判断对象的一致性 162 */ 163 public class Account 164 / 账户编码 165 private String accountNo; 166 / 账户余额 167 private double balance; 168 / 标示账户是否已有存款(此项目为了测试存入款就需要马上取出) 169 private boolean flag = false; 170 / private final Lock lock=new ReentrantLock(); 171 / private final Condition cond=lock.newCondition(); 172 public Account() 173 super(); 174 175 public Account(String accountNo, double balance) 176 super(); 177 this.accountNo = accountNo; 178 this.balance = balance; 179 180 / 取款(利用同步方法) 181 public synchronized void draw(double drawAmount) 182 / 如果flag为假,没人存款进去,取钱方法(利用wait)阻塞(wait阻塞时,当前线程会释放同步监视器) 183 try 184 if (!flag) 185 this.wait();/条件 cond.await(); 186 187 /否则执行取钱 188 else 189 / System.out.println(账户余额:+balance); 190 System.out.println(Thread.currentThread().getName()+-取钱:+drawAmount); 191 balance-=drawAmount; 192 System.out.println(账户余额: +balance); 193 /设置flag(限定一个操作只能取一次钱) 194 flag=false; 195 /唤醒其他wait()线程 196 this.notifyAll();/cond.signalAll(); 197 198 catch (InterruptedException e) 199 e.printStackTrace(); 200 201 202 /存款 203 public synchronized void deposit(double depositAmount) 204 /如果flag为真,证明有人存钱了,存钱阻塞 205 try 206 if (flag) 207 this.wait(); /cond.await(); 208 209 /否则执行存款 210 else 211 / System.out.println(账户余额:+balance); 212 System.out.println(Thread.currentThread().getName()+-存钱:+depositAmount); 213 balance+=depositAmount; 214 System.out.println(账户余额: +balance); 215 /设置flag(限定一个操作只能取一次钱) 216 flag=true; 217 /唤醒其他wait()线程 218 this.notifyAll(); /cond.signalAll(); 219 220 catch (InterruptedException e) 221 e.printStackTrace(); 222 223 224 / DDD设计模式重写equals和hashcode(判断用户是否一致,只需要判断他们的账号编码就可以了,不需要再判断整个对象,提高性能) 225 Override 226 public int hashCode() 227 return accountNo.hashCode(); 228 229 Override 230 public boolean equals(Object obj) 231 if (obj != null & obj.getClass() = Account.class) 232 Account account = (Account) obj; 233 return account.getAccountNo().equals(accountNo); 234 235 return false; 236 237 public String getAccountNo() 238 return accountNo; 239 240 public void setAccountNo(String accountNo) 241 this.accountNo = accountNo; 242 取钱线程:java view plaincopy243 public class DrawThread implements Runnable 244 /* 245 * 模拟用户 246 */ 247 private Account account; 248 /用户取钱数 249 private double drawAmount; 250 public DrawThread(Account account, double drawAmount) 251 super(); 252 this.account = account; 253 this.drawAmount = drawAmount; 254 255 Override 256 public void run() 257 /重复10次取钱操作 258 for(int i=0;i10;i+) 259 account.draw(drawAmount); 260 261 262 存钱线程:java view plaincopy263 public class DepositThread implements Runnable 264 /* 265 * 模拟用户 266 */ 267 private Account account; 268 /用户存钱数 269 private double depositAmount; 270 public DepositThread(Account account, double depositAmount) 271 super(); 272 this.account = account; 273 this.depositAmount = depositAmount; 274 275 Override 276 public void run() 277 /重复10次存钱操作 278 for(int i=0;i存钱:800.0 297 账户余额: 800.0 298 取款者-取钱:800.0 299 账户余额: 0.0 300 存款者丙-存钱:800.0 301 账户余额: 800.0 302 取款者-取钱:800.0 303 账户余额: 0.0 304 存款者甲-存钱:800.0 305 账户余额: 800.0 306 取款者-取钱:800.0 307 账户余额: 0.0 308 存款者丙-存钱:800.0 309 账户余额: 800.0 310 取款者-取钱:800.0 311 账户余额: 0.0 312 存款者甲-存钱:800.0 313 账户余额: 800.0 314 取款者-取钱:800.0 315 账户余额: 0.0 316 存款者丙-存钱:800.0 317 账户余额: 800.0 但根据上面情况来看,显示用户被阻塞无法继续向下执行,这是因为存钱有三个线程 共有3*10=30次操作,而取钱只有10次,所以阻塞。阻塞和死锁是不一致的,这里阻塞只是在等待取钱。3.使用管道流(通信)上面的1,2两种线程操作,与其称为线程间的通信,不如称为线程之间协调运行的控制策略还要恰当些。如果需要在两条线程之间惊醒更多的信息交互,则可以考虑使用管道流进行通信。管道流有3中形式: 1.字节流:PipedInputStream,PipedOutputStream 2.字符流:PipedReader,PipedWriter 3.新IO的管理Channel:Pipe.SinkChannel,Pipe.SourceChannel使用管道的步骤: 1.new创建管道输入输出流 2.使用管道输入或输出流的connect方法连接这两个输入输出流 3.将两个管道流分别传入两个线程 4.两个线程分别依赖各自的流来进行通信但是因为两个线程属于同一个进程,它们可以非常方便的共享数据,利用共享这个方式才应该是线程之间进行信息交流的最好方式,而不是使用管道流。如果是操作诸如聊天室那样的话,用管道通信效果会好些,共享资源的话,性能太低,出错概率高。CODE:java view plaincopy318 /*读取管道中的信息线程*/ 319 public class ReaderThread implements Runnable 320 private PipedReader pr; 321 private BufferedReader br; 322 public ReaderThread() 323 super(); 324 325 public ReaderThread(PipedReader pr) 326 super(); 327 this.pr = pr; 328 /包装管道流 329 this.br=new BufferedReader(pr); 330 331 public void run() 332 String buffer=null; 333 System.out.println(Thread.currentThread().getName(); 334 try 335 /开始逐行读取管道流数据(假定管道流数据时字符流) 336 System.out.println(-打印管道中的数据-); 337 while(buffer=br.readLine()!=null) 338 System.out.println(buffer); 339 340 341 catch(IOException e) 342 e.printStackTrace(); 343 344 finally 345 try 346 if(br!=null) 347 br.close(); 348 catch (IOException e) 349 e.printStackTrace(); 350 351 352 353 java view plaincopy354 /*像管道流中写入数据*/ 355 public class WriterThread implements Runnable 356 /定义一个数组来充当向管道中输入数据 357 String str=new S论坛,2.谷歌,3.Hibernate,4.虾米; 358 private PipedWriter pw; 359 public WriterThread() 360 361 public WriterThread(PipedWriter pw) 362 this.pw = pw; 363 364 public void run() 365 System.out.println(Thread.currentThread().getName(); 366 try 367 /向管道流中写入数据,以供读取 368 for(int i=0;i100;i+) 369 pw.write(stri%4+n); 370 371 372 catch(IOException e) 373 e.printStackTrace(); 374 375 finally 376 try 377 if(pw!=null) 378 pw.close(); 379 380 catch (IOException e) 381 / TODO Auto-generated catch block 382 e.printStackTrace(); 383 384 385 386 java view plaincopy387 public class TestPiped 388 public static void main(String args) 389 PipedReader pr = null; 390 PipedWriter pw = null; 391 try 392 pw = new PipedWriter(); 393 pr = new PipedReader(); 394 / 链接管道 395 pr.connect(pw); 396 new Thread(new ReaderThread(pr),读取管道线程).start(); 397 new Thread(new WriterThread(pw),写入管道线程).start(); 398 catch (IOException e) 399 e.printStackTrace(); 400 401 402 403 结果: 404 读取管道线程 405 -打印管道中的数据- 406 写入管道线程 407 1.论坛 408 2.谷歌 409 3.Hibernate 410 4.虾米 411 1.论坛 412 2.谷歌 413 3.Hibernate 414 4.虾米 415 1.论坛 416 .(一共100条) 六.线程组(ThreadGroup)1.线程组介绍: 线程组可以对一批线程进行分类管理,Java也允许程序直接对线程组进行控制。对线程组的控制相当于同时控制这批线程。用户创建的所有线程都属于指定线程组,如果没有显示指定线程属于哪个线程组,那么这个线程属于默认线程组。在默认情况下,子线程和创建它的父线程处于同一个线程组内:例如A创建B线程,B没有指定线程组,那么A和B属于同一个线程组。 一旦某个线程加入到指定线程组,那么该线程就一直属于该线程组,直到该线程死亡,线程运行中途不能改变它所属的线程组(中途不能改变线程组,所以Thread类只有getThreadGroup()方法来获得线程组,而没有set方法。)。 java view plaincopy417 public final ThreadGroup getThreadGroup() 418 return group; 419 Thread类中构造方法:其中参数的个数根据情况而定,init中会根据参数个数而变,没这个参数的就直接null long类型就0. java view plaincopy420 public Thread(ThreadGroup group, Runnable target, String name, 421 long stackSize) 422 init(group, target, name, stackSize); 423 ThreadGroup类中的构造方法:java view plaincopy424 /不属于任何一个线程组,是一个必须的用来创建系统线程的组 425 /* 426 * Creates an empty Thread group that is not in any Thread group. 427 * This method is used to create the system Thread group. 428 */ 429 private ThreadGroup() / called from C code 430 = system; 431 this.maxPriority = Thread.MAX_PRIORITY; 432 this.parent = null; 433 434 - 435 private ThreadGroup(Void unused, ThreadGroup parent, String name) 436 = name; 437 this.maxPriority = parent.maxPriority; 438 this.daemon = parent.daemon; 439 this.vmAllowSuspension = parent.vmAllowSuspension; 440 this.parent = parent; 441 parent.add(this); 442 443 /指定线程组名创建一个线程组 444 public ThreadGroup(String name) 445 this(Thread.currentThread().getThreadGroup(), name); 446 447 /指定的父线程和线程组名来创建一个线程组 448 public ThreadGroup(ThreadGroup parent, String name) 449 this(checkParentAccess(parent), parent, name); 450 看出上面两个public构造器都需要指定一个名字给线程组,所以线程组总是具有一个字符串名字,该名称可调用ThreadGroup的getName()获得,但不允许改变线程组的名字。 源码setMaxPriority(int pri) java view plaincopy451 public final void setMaxPriority(int pri) 452 int ngroupsSnapshot; 453 ThreadGroup groupsSnapshot; 454 synchronized (this) 455 checkAccess(); 456 if (pri Thread.MAX_PRIORITY) 457 return; 458 459 maxPriority = (parent != null) ? Math.min(pri, parent.maxPriority) : pri; 460 ngroupsSnapshot = ngroups; 461 if (groups != null) 462 groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot); 463 else 464 groupsSnapshot = null; 465 466 467 for (int i = 0 ; i ngroupsSnapshot ; i+) 468 groupsSnapshoti.setMaxPriority(pri); 469 470 例子:java view plaincopy471 /*测试线程组*/ 472 public class TestThread implements Runnable 473 /指定线程组 474 Override 475 public void run() 476 for(int i=0;i10;i+) 477 System.out.println(Thread.currentThread().getName()+线程+i+属于+Thread.currentThread().getThre
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年北京平谷区卫生健康委员会所属事业单位第二次招聘211人笔试备考题库附答案详解
- 初中道德与法治大单元教学探索与实践
- 2024火电电力职业鉴定模拟题库及参考答案详解(能力提升)
- 水库枢纽工程招商引资报告
- 机械加工教学模式改革创新研究
- 2024年临床执业医师考前冲刺练习试题【典型题】附答案详解
- 100万千瓦风电项目规划设计方案
- 易腐垃圾资源化综合体项目投资计划书
- 自考专业(计算机网络)高频难、易错点题附参考答案详解(考试直接用)
- 2024年安全监察人员能力检测试卷(A卷)附答案详解
- 工具模型-尤里奇2021年版新HR胜任力
- 新能源汽车检测与维修专业调研报告
- 2024年保安员证考试题库及答案(共240题)
- 哲学与人生 第二课 树立科学的世界观2.1
- 华中师大版八年级全一册心理健康 1.别让时间悄悄溜走 教案
- 母婴保健技术服务人员考核审批表
- 新大象版六年级上册科学全册复习专用知识点
- 2024年印度混凝土搅拌车行业状况及未来发展趋势报告
- 养老院餐饮供应服务行业发展全景调研与投资趋势预测研究报告
- HYT 0288-2020 宗海价格评估方法(报批标准名称:海域价格评估技术规范)(正式版)
- 《学会聆听(第一课时)》教学课件
评论
0/150
提交评论