Java多线程基础

Thread类的run方法和start方法

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

线程的启动

利用Thread类的子类
  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. public static void main(String[] args {
  12. new PrintThread("Good!").start();
  13. new PrintThread("Nice!").start();
  14. }
  15. }
在main()方法里,先建立PrintThread类的实例后,调用该实例的 start()方法启动线程。建立“PrintThread类的实例”和“启动该实例所对应的线程”是两个完全不同的处理。即使已经建立了实例,仍然必须 等到调用start()方法才会启动线程。主线程在main()方法里启动两个线程,因为main()方法会立即结束,所以主线程也会立即结束,不过整个 程序还没有结束,一直要等到所有线程都已经结束,程序才会结束。不过这里不包括daemon thread。利用Runnable接口
Runnable接口是java.lang Package里的接口,声明方法如下:

public interface Runnable {public abstract void run();}已实现Runnable接口的类必须实现run()方法。

  1. public class PrintThread implements Runnable {
  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. public static void main(String[] args {
  12. new Thread(new PrintThread("Good!")).start();
  13. new Thread(new PrintThread("Nice!")).start();
  14. }
  15. }
不管是利用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类:

  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. public class Gate {
  11. private int counter = 0;
  12. private String name = "Nobody";
  13. private String address = "Nowhere";
  14. public void pass(String name, String address) {
  15. this.counter++;
  16. this.name = name;
  17. this.address = address;
  18. check();
  19. }
  20. public String toString() {
  21. return "No. " + counter + " name: " + name + ", address: " + address;
  22. }
  23. private void check() {
  24. if (name.charAt(0) != address.charAt(0)) {
  25. System.out.println("******BROKEN*******" + toString());
  26. }
  27. }
  28. public class UserThread extends Thread {
  29. private final Gate gate;
  30. private final String myname;
  31. private final String myaddress;
  32. public UserThread (Gate gate, String myname, String myaddress) {
  33. this.gate = gate;
  34. this.myname = myname;
  35. this.myaddress =myaddress;
  36. }
  37. public void run() {
  38. System.out.println(this.myname + "Begin");
  39. while(true) {
  40. gate.pass(this.myname,myaddress);
  41. }
  42. }
  43. }
  44. }

执行看看。
由于Gate类不是线程安全的,当多个线程对其的状态进行更改时,会出现与期望不符的结果。可以通过将Gate类改造成线程安全的类来解决这个问题。线程安全最简单的方法即是使用本模式,使同一时间只让一个线程执行。线程安全版的Gate类如下:

  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类

  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. public class Main() {
  19. public static void main(String[] args){
  20. Person alice = new Person("Alice", "Alaska");
  21. new PrintPersonThread(alice).start();
  22. new PrintPersonThread(alice).start();
  23. new PrintPersonThread(alice).start();
  24. }
  25. }
  26. public class PrintPersonThread extends Thread {
  27. private Person person;
  28. public PrintPersonThread(Person persion) {
  29. this.person = person;
  30. }
  31. public void run() {
  32. while(true) {
  33. System.out.println(Thread.currentThread().getName() + " prints " + person);
  34. }
  35. }
  36. }

Immutable Pattern的参与者为不变者。Immutable
Pattern何时适用,当实例产生后,状态不再变化;实例需要共享,而且访问频繁时。Java语言的标准类库中有许多使用Immutable的类,例
如:java.lang.String、java.lang.Integer\java.lang.Short这些基本类型的包装类。

转载自:http://blog.csdn.net/shenzhen_liubin/article/details/9736485

最新文章

  1. 小菜学习设计模式(三)—工厂方法(Factory Method)模式
  2. linux下的redis安装以及php添加redis扩展
  3. 【POJ 2826】An Easy Problem?!(几何、线段)
  4. 对于Eclipse的正确用法
  5. CentOS6.3下安装VSFTP服务
  6. Python中pip版本升级error:You are using pip version 7.1.2, however version 8.1.1 is available.
  7. cojs 简单的区间问题 解题报告
  8. JavaScript之可运行按钮
  9. FusionCharts(v3.6.0)使用(1)
  10. Python建立SSH连接与使用方法
  11. Python迭代
  12. 编译预处理命令define
  13. SQL SERVER FOR LINUX初体验
  14. JVM性能参数调优实践,不会执行Full GC,网站无停滞
  15. ECMAScript 6之变量的解构赋值
  16. java代码块,静态代码块,静态变量,构造方法执行顺序
  17. 邮票面值设计 (动态规划+DFS)
  18. Cassandra集群:一,搭建一个三节点的集群
  19. ubuntu 窗口操作快捷键
  20. java语言编程入门

热门文章

  1. python开发_自己开发的一个小游戏
  2. MySQL多实例介绍
  3. Netty生产级的心跳和重连机制
  4. CSS——操作css文件
  5. [转] 基于C#的波形显示控件的实现
  6. 转 查看linux文件目录的大小和文件夹包含的文件数
  7. FFmpeg库简介
  8. spring boot 项目打包到maven仓库供其它模块使用
  9. jena读取和解析本体文件
  10. 10+ 最佳的 Node.js 教程和实例