java多线程设计模式——学习笔记(2)Single Threaded Execution Pattern
2014-05-06 19:51
381 查看
Single Threaded Execution Pattern是多线程中最为简单的一种模式,其用来限制同时只能让一个线程运行,用于多个线程共享资源(sharedResource)的情况。
文章中举了一个例子来说明该模式的使用。程序模拟3个人频繁经过只能同时通过一个人的门,当人通过门时,程序会在计数器中递增通过人数,并记录通过人员的姓名和地址。
程序中使用的类如下表所示:
首先是不使用Single Threaded Execution Pattern的情况:
Main类:
非线程安全(Thread-safe)的Gate类:
UserThread类:
运行结果:
**** BROKEN ***** No.654810203: Alice, Alaska
***** BROKEN ***** No.654810878: Chris, Canada
***** BROKEN ***** No.654811529: Chris, Alaska
***** BROKEN ***** No.654802550: Bobby, Canada
***** BROKEN ***** No.654812495: Alice, Brazil
***** BROKEN ***** No.654811529: Chris, Alaska
***** BROKEN ***** No.654813485: Chris, Canada
***** BROKEN ***** No.654814227: Alice, Alaska
***** BROKEN ***** No.654814972: Alice, Alaska
***** BROKEN ***** No.654815613: Alice, Alaska
可以看到,运算结果出现了错误,一种情况是即使姓名和地址的首字母一致,但还是打印出了BROKEN,另一种情况就是姓名和地址的首字母不一致的情况。
为什么会出现这种情况?
因为pass方法可以同时被多个线程调用,多个线程调用pass方法时,里面的语句可能是交错运行的,这就有可能出现姓名和地址的首字母不一致的情况。情况有可能如下图所示:
因为,所有线程都是调用同一个pass方法,它们之间是共享的关系,为了避免上述情况的发生,就必须保证同一时间只能有一个线程在访问pass方法。因此,可以将Gate类改为线程安全的类:
先来看下运行结果:
Testing Gate, hit CTRL+C to exit.
Alice BEGIN
Bobby BEGIN
Chris BEGIN
(无论等多久,都不会打印出BROKEN)
运行结果正确。通过比较两个Gate类,可以发现,线程安全的Gate类只改了一点点,就是在pass()方法和toString()方法前面加上了synchronized关键字,因为synchronized方法可以保证同一时间只有一个线程可以执行它。
这样我们就完成了一个Single Threaded Execution Pattern。当然这过程中我们还需要注意几点:
死锁
所谓死锁,就是指两个线程分别获取了锁定,互相等待另一个线程解除锁定的现象。举个例子,Alice和Bobby同吃一盘意大利面,桌上只有一只汤勺和一只叉子,吃意大利面时,必须同时使用汤勺和叉子,某一时刻,Alice拿着汤勺,Bobby拿着叉子,它们都在等待对方放下手中的餐具,这样,你等我,我等你,就陷入了死锁的情况。
当满足以下条件时,就可能出现死锁的情况:
(1)多个sharedResource参与者,相当于汤勺和叉子
(2)线程锁定一个sharedResource时,还没解除就去锁定另一个sharedResource。就相当于握着汤勺想着去拿对方的叉子
(3)sharedResource的角色是对等的,就像“拿汤勺—>拿叉子”和“拿叉子—>拿汤勺”这两个动作可能同时发生
(1)(2)(3)中只要破坏一种条件,就可避免死锁
2. 该以什么单位保护
对于Gate类,name和Address非得合在一起赋值不可,将pass方法定义成synchronized方法,就是为了防止多个线程穿插执行赋值语句,如果单独定义setName,setAddress这样的synchronized方法,线程对字段赋值的操作就被分散了,就不安全了
差不多就是这些了,这个模式算是比较简单了,后面还有更难的模式等着我呢,继续加油吧!
文章中举了一个例子来说明该模式的使用。程序模拟3个人频繁经过只能同时通过一个人的门,当人通过门时,程序会在计数器中递增通过人数,并记录通过人员的姓名和地址。
程序中使用的类如下表所示:
名称 | 解说 |
Main | 创建一个门,并操作三个人不断的穿越门的类 |
Gate | 表示门的类,当人经过时会记录人的姓名和地址 |
UserThread | 表示人的类,只负责处理不断的在门间穿越 |
Main类:
public class Main { public static void main(String[] args) { System.out.println("Testing Gate, hit CTRL+C to exit."); Gate gate = new Gate(); new UserThread(gate, "Alice", "Alaska").start(); new UserThread(gate, "Bobby", "Brazil").start(); new UserThread(gate, "Chris", "Canada").start(); } }
非线程安全(Thread-safe)的Gate类:
public class Gate { private int counter = 0; private String name = "Nobody"; private String address = "Nowhere"; public void pass(String name, String address) { this.counter++; this.name = name; this.address = address; check(); } public String toString() { return "No." + counter + ": " + name + ", " + address; } private void check() { if (name.charAt(0) != address.charAt(0)) { System.out.println("***** BROKEN ***** " + toString()); } } }其中check()方法是来检测门的状态的,如果姓名和地址的首字母不一致,则表示记录有问题,打印出BROKEN。
UserThread类:
package com.hik.test; public class UserThread extends Thread { private final Gate gate; private final String myname; private final String myaddress; public UserThread(Gate gate, String myname, String myaddress) { this.gate = gate; this.myname = myname; this.myaddress = myaddress; } public void run() { System.out.println(myname + " BEGIN"); while (true) { gate.pass(myname, myaddress); } } }
运行结果:
**** BROKEN ***** No.654810203: Alice, Alaska
***** BROKEN ***** No.654810878: Chris, Canada
***** BROKEN ***** No.654811529: Chris, Alaska
***** BROKEN ***** No.654802550: Bobby, Canada
***** BROKEN ***** No.654812495: Alice, Brazil
***** BROKEN ***** No.654811529: Chris, Alaska
***** BROKEN ***** No.654813485: Chris, Canada
***** BROKEN ***** No.654814227: Alice, Alaska
***** BROKEN ***** No.654814972: Alice, Alaska
***** BROKEN ***** No.654815613: Alice, Alaska
可以看到,运算结果出现了错误,一种情况是即使姓名和地址的首字母一致,但还是打印出了BROKEN,另一种情况就是姓名和地址的首字母不一致的情况。
为什么会出现这种情况?
因为pass方法可以同时被多个线程调用,多个线程调用pass方法时,里面的语句可能是交错运行的,这就有可能出现姓名和地址的首字母不一致的情况。情况有可能如下图所示:
因为,所有线程都是调用同一个pass方法,它们之间是共享的关系,为了避免上述情况的发生,就必须保证同一时间只能有一个线程在访问pass方法。因此,可以将Gate类改为线程安全的类:
public class Gate { private int counter = 0; private String name = "Nobody"; private String address = "Nowhere"; public synchronized void pass(String name, String address) { this.counter++; this.name = name; this.address = address; check(); } public synchronized String toString() { return "No." + counter + ": " + name + ", " + address; } private void check() { if (name.charAt(0) != address.charAt(0)) { System.out.println("***** BROKEN ***** " + toString()); } } }
先来看下运行结果:
Testing Gate, hit CTRL+C to exit.
Alice BEGIN
Bobby BEGIN
Chris BEGIN
(无论等多久,都不会打印出BROKEN)
运行结果正确。通过比较两个Gate类,可以发现,线程安全的Gate类只改了一点点,就是在pass()方法和toString()方法前面加上了synchronized关键字,因为synchronized方法可以保证同一时间只有一个线程可以执行它。
这样我们就完成了一个Single Threaded Execution Pattern。当然这过程中我们还需要注意几点:
死锁
所谓死锁,就是指两个线程分别获取了锁定,互相等待另一个线程解除锁定的现象。举个例子,Alice和Bobby同吃一盘意大利面,桌上只有一只汤勺和一只叉子,吃意大利面时,必须同时使用汤勺和叉子,某一时刻,Alice拿着汤勺,Bobby拿着叉子,它们都在等待对方放下手中的餐具,这样,你等我,我等你,就陷入了死锁的情况。
当满足以下条件时,就可能出现死锁的情况:
(1)多个sharedResource参与者,相当于汤勺和叉子
(2)线程锁定一个sharedResource时,还没解除就去锁定另一个sharedResource。就相当于握着汤勺想着去拿对方的叉子
(3)sharedResource的角色是对等的,就像“拿汤勺—>拿叉子”和“拿叉子—>拿汤勺”这两个动作可能同时发生
(1)(2)(3)中只要破坏一种条件,就可避免死锁
2. 该以什么单位保护
对于Gate类,name和Address非得合在一起赋值不可,将pass方法定义成synchronized方法,就是为了防止多个线程穿插执行赋值语句,如果单独定义setName,setAddress这样的synchronized方法,线程对字段赋值的操作就被分散了,就不安全了
差不多就是这些了,这个模式算是比较简单了,后面还有更难的模式等着我呢,继续加油吧!
相关文章推荐
- 多线程程序设计学习(2)之single threaded execution pattern
- 多线程模式之-single Threaded Execution Pattern
- 多线程学习系列 - 1 - Single Threaded Execution Pattern
- Single Threaded Execution----java多线程设计模式(一)
- JAVA并发设计模式学习笔记(二)—— Single Threaded Execution Pattern
- 多线程学习之一独木桥模式Single Threaded Execution Pattern
- 线程之旅--Single Threaded Execution Pattern
- java多线程之Single Threaded Execution模式
- Java多线程设计模式详解学习笔记三——SingleThreadExecution
- JAVA多线程(三)模式-Single Threaded Execution
- 第一章 Single Threaded Execution
- 多线程设计模式(一) Single Threaded Execution
- Single Threaded Execution----能通过这座桥的,只有一个人
- Concurrent Programming in Java - Single Threaded Execution
- Java中的线程(八)- Single Threaded Execution模式
- Single Threaded Execution
- Single Threaded Execution
- 单件模式(Single Pattern)
- 模式1--SingleThreadExecution
- javathreadpattern_01_singleThreadPattern