• Java多线程设计模式(一)


     

    Java多线程基础

    Thread类的run方法和start方法

           Java语言写成的程序一定是先从主线程开始操作,所以必须在程序的某个位置启动新的线程,才能算是真正的多线程程序。start()方法是 Thread类的方法,调用start()方法,就会启动新的线程。请注意,被调用来启动线程的是start()方法,而非run()方法。调用 start()方法之后,Java执行处理系统会在背后启动新的线程。再由这个新的线程调用run()方法。调用start()方法,会有两个操作:
    • 启动新的线程
    • 调用run方法

    线程的启动

    利用Thread类的子类
    [java] view plaincopy
     
    1. public class PrintThread extends Thread {  
    2.       private String msg;  
    3. public PrintThread(String msg) {  
    4. this.msg = msg;  
    5. }  
    6. public void run() {  
    7. for(int i = 0; i < 10000; i++) {  
    8. System.out.print(msg);  
    9. }  
    10. }  
    11.   
    12. public static void main(String[] args {  
    13. new PrintThread("Good!").start();  
    14. new PrintThread("Nice!").start();  
    15. }  
    16. }  
    在main()方法里,先建立PrintThread类的实例后,调用该实例的 start()方法启动线程。建立“PrintThread类的实例”和“启动该实例所对应的线程”是两个完全不同的处理。即使已经建立了实例,仍然必须 等到调用start()方法才会启动线程。主线程在main()方法里启动两个线程,因为main()方法会立即结束,所以主线程也会立即结束,不过整个 程序还没有结束,一直要等到所有线程都已经结束,程序才会结束。不过这里不包括daemon thread。利用Runnable接口
    Runnable接口是java.lang Package里的接口,声明方法如下:
    public interface Runnable {public abstract void run();}已实现Runnable接口的类必须实现run()方法。
    [java] view plaincopy
     
    1.    
    2. public class PrintThread implements Runnable {  
    3. private String msg;  
    4. public PrintThread(String msg) {  
    5. this.msg = msg;  
    6. }  
    7. public void run() {  
    8. for(int i = 0; i < 10000; i++) {  
    9. System.out.print(msg);  
    10. }  
    11. }  
    12.   
    13. public static void main(String[] args {  
    14. new Thread(new PrintThread("Good!")).start();  
    15. new Thread(new PrintThread("Nice!")).start();  
    16. }  
    17. }  
    不管是利用Thread类的子类还是利用Runnable接口的实现类来启动线程,都是通过Thread类的start()方法。

    线程的暂时停在

    利用Thread类的sleep()方法即可暂时停在线程的执行操作。注意,sleep()方法是Thread类的静态方法。

    线程的共享互斥

    synchronized方法
    当 一个方法加上关键字synchronized声明之后,就可以让1个线程操作这个方法。这种线程称为synchronized方法,又称为同步方法。 synchronized实例方法就是使用this锁定去做线程的共享互斥。synchronized类方法是使用该类的类对象的锁定去做线程的共享互斥

    线程的协调

    所 有实例都有一个wait set,wait set是一个在执行该实例的wait方法时、操作停止的线程的集合。一个执行wait()方法时,线程便会暂时停止操作,进入wait set这个休息室。如欲执行wait()方法,线程需获取锁定。但是当线程进入wait set时,已经释放了该实例的锁定。使用notify()方法时,可从wait set里抓取一个线程。线程必须有调用实例的锁定,才能执行notify()方法,这跟调用wait()方法一样。使用notifyAll()方法时,会 将所有在wait set里等待的线程全部拿出来。同样,线程必须获取调用实例的锁定,才能调用notifyAll()方法。注意,wait()、notify()、 notifyAll()方法都是Object类的方法。

    Single Threaded Execution Pattern

    使用该模式来限制同时只让一个线程运行。先看一个不是使用该模式的多线程的例子,并非线程安全(Thread-safe)的Gate类:
    [java] view plaincopy
     
    1. public class Main {  
    2. public static void main(String[] args) {  
    3. System.out.println("Testing Gate, hit CTRC+C to exit.");  
    4. Gate gate = new Gate();  
    5. new UserThread(gate, "Alice""Alaska").start();  
    6. new UserThread(gate, "Bobby""Brazil").start();  
    7. new UserThread(gate, "Chris""Canada").start();  
    8. }  
    9. }  
    10.   
    11. public class Gate {  
    12. private int counter = 0;  
    13. private String name = "Nobody";  
    14. private String address = "Nowhere";  
    15. public void pass(String name, String address) {  
    16. this.counter++;  
    17. this.name = name;  
    18. this.address = address;  
    19. check();  
    20. }  
    21. public String toString() {  
    22. return "No. " + counter + " name: " + name + ", address: " + address;  
    23. }  
    24. private void check() {  
    25. if (name.charAt(0) != address.charAt(0)) {  
    26. System.out.println("******BROKEN*******" + toString());  
    27. }  
    28. }  
    29.   
    30. public class UserThread extends Thread {  
    31. private final Gate gate;  
    32. private final String myname;  
    33. private final String myaddress;  
    34. public UserThread (Gate gate, String myname, String myaddress) {  
    35. this.gate = gate;  
    36. this.myname = myname;  
    37. this.myaddress =myaddress;  
    38. }  
    39. public void run() {  
    40. System.out.println(this.myname + "Begin");  
    41. while(true) {  
    42. gate.pass(this.myname,myaddress);  
    43. }  
    44. }  
    45. }  
    46. }  
    执行看看。
    由于Gate类不是线程安全的,当多个线程对其的状态进行更改时,会出现与期望不符的结果。可以通过将Gate类改造成线程安全的类来解决这个问题。线程安全最简单的方法即是使用本模式,使同一时间只让一个线程执行。线程安全版的Gate类如下:
    [java] view plaincopy
     
    1. public class Gate {  
    2. private int counter = 0;  
    3. private String name = "Nobody";  
    4. private String address = "Nowhere";  
    5. public synchronized void pass(String name, String address) {  
    6. this.counter++;  
    7. this.name = name;  
    8. this.address = address;  
    9. check();  
    10. }  
    11. public synchronized String toString() {  
    12. return "No. " + counter + " name: " + name + ", address: " + address;  
    13. }  
    14. private void check() {  
    15. if (name.charAt(0) != address.charAt(0)) {  
    16. System.out.println("******BROKEN*******" + toString());  
    17. }  
    18. }  
    19. }  

    即在pass()方法和toString()方法前面加上synchronized关键字,这样Gate类就是线程安全的类了。synchronized锁扮演的角色就是对共享资源的保护。
    Single Threaded Execution Pattern的参与者:
    SharedResource(共享资源):在本例中Gate类(准确说是Gate类的实例)是这个SharedResource。 SharedResource是可由多个线程访问的类。在该模式下,我们对unsafeMethod加以防护,限制同时只能有一个线程进行访问,在 Java语言中,将unsafeMethod定义成synchronized方法,就可以实现这个目标。这个必须只让单线程执行的程序范围,我们称为临界 区(critical section)
    何时该适用Single Threaded Execution Pattern,当SharedResouce实例可能同时被多个线程访问的时候,并且SharedResource的状态可能变化的时候。
    另外注意,使用Single Threaded Execution Pattern 时可能会发生死锁(deadlock)的危险。
    性能问题,临界区的大小与执行性能直接相关。首先,获取锁定需要花费时间,其次,线程冲突时必须等待。所以,尽可能缩小临界区的范围,以减少出现线程冲突的机会,可抑制性能的降低。
    另外一个问题,synchronized是获取谁的锁定来保护呢?如果实例不同,那么锁定也不同。如果有多个不同的实例,那么多个线程仍然可以分别执行不同实例的synchronized方法。
    synchronized方法同时只有一个线程可以执行,当有一个线程正在执行synchronized方法时,其他线程不能进入这个方法。从多线程的角 度看,synchronized方法是原子操作(atomic operation)。在Java语言规格上,long和double的赋值操作并不是原子的。可以在类属性字段前面加上volatile关键字将所有对 该字段的操作变为原子的。

    Immutable Pattern

    不变模式,该模式的语义与GoF定义的设计模式的不变模式是一样的,即通过定义不变类,来实现线程的安全性。由于类的实例一旦生成,其状态将不会变化,顾其天生就是线程安全的。
    使用Immutable Pattern 的Person类
    [java] view plaincopy
     
    1. public final class Person {  
    2. private final String name;  
    3. private final String address;  
    4. public Person(String name, String address) {  
    5. this.name = name;  
    6. this.address = address;  
    7. }  
    8. public String getName() {  
    9. return this.name;  
    10. }  
    11. public String getAddress() {  
    12. return this.address;  
    13. }  
    14. public String toString() {  
    15. return "[ Person: name =" + name + ", address = " + address + " ]";  
    16. }  
    17. }  
    18.   
    19. public class Main() {  
    20. public static void main(String[] args){  
    21. Person alice = new Person("Alice""Alaska");  
    22. new PrintPersonThread(alice).start();  
    23. new PrintPersonThread(alice).start();  
    24. new PrintPersonThread(alice).start();  
    25. }  
    26. }  
    27.   
    28. public class PrintPersonThread extends Thread {  
    29. private Person person;  
    30. public PrintPersonThread(Person persion) {  
    31. this.person = person;  
    32. }  
    33. public void run() {  
    34. while(true) {  
    35. System.out.println(Thread.currentThread().getName() + " prints " + person);  
    36. }  
    37. }   
    38. }  

    Immutable Pattern的参与者为不变者。Immutable Pattern何时适用,当实例产生后,状态不再变化;实例需要共享,而且访问频繁时。Java语言的标准类库中有许多使用Immutable的类,例 如:java.lang.String、java.lang.Integerjava.lang.Short这些基本类型的包装类。
    转载自:http://blog.csdn.net/shenzhen_liubin/article/details/9736485
  • 相关阅读:
    HTML 块级、内联、内联块级元素(转)
    CSS margin-top父元素下落
    JS 窗口resize避免触发多次
    ASP.NET 自制免费.NET代码生成器KevinCodeBuilder
    Plugin 中国省市选择插件
    Git 常用命令(转)
    JS Regex正则表达式的使用(转)
    Git 详细中文安装教程(转)
    业务逻辑中的测试总结(一)----比值类需求测试分解
    python学习笔记系列----(四)模块
  • 原文地址:https://www.cnblogs.com/chenying99/p/3322032.html
Copyright © 2020-2023  润新知