分享

多线程

 360doc_kim 2012-07-18
线程相关知识
创建和启动; 调度和优先级; 状态控制; 线程同步。

java编程语言使多线程如此简单有效,以致于某些程序员说它实际上是自然的。
要记住的一件重要的事情是 main() 函数也是一个线程,并可用来做有用的工作。程序员只有在需要多个线程时才需要创建新的线程。
在Java中创建线程有两种方法:使用Thread类和使用Runnable接口。
在使用Runnable接口时需要建立一个Thread实例。因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或它的子类的实例。
最好采用实现Runnable接口的方式,创建线程类,因为使用接口之后,该类还可以继承别的类。



   

  
1.继承Thread 类
Thread 类是一个具体的类,即不是抽象类,该类封装了线程的行为。
要创建一个线程,
1.创建一个从 Thread 类导出的新类。
2.覆盖(override) Thread 的 run() 函数来完成有用的工作。
3.实例化新建类。
4.用户并不直接调用此run()函数;而是必须调用 Thread 的 start() 函数,该函数再调用 run()。

  start()使run()运行之后,线程2开始独立地运行。

下面的代码说明了它的用法:
  创建两个新线程

在这里调用了start()后,并不是直接就运行run()方法中的操作,而是进入到就绪(runnable state)状态。等待被分配机器资源,进入运行(running)状态,才运行run()中的操作

class Count extends Thread {
        public void start() {
                System.out.println("** Runnable ****");
                super.start();
        }

        public void run() {
                System.out.println("** running *****");
                for (int i=0; i <= 5; i++) {
                        System.out.println("Count: " + i);
                }
                System.out.println("** Dead ********");
        }
}

class CountTest {
        public static void main(String[] args) {
                Count thread = new Count();
                System.out.println("** New Thread **");
                thread.start();

                System.out.println("last line in main()");
        }
}   

结果
C:\Java\Thread>javac CountTest.java

C:\Java\Thread>java CountTest
** New Thread **
** Runnable ****
last line in main()
** running *****
Count: 0
Count: 1
Count: 2
Count: 3
Count: 4
Count: 5
** Dead ********

C:\Java\Thread>

有结果可知调用start()方法,并不是立刻执行run()方法中的内容。而是等待机器给它分配资源,才能开始运行。

例子


import java.util.*;

class TimePrinter extends Thread {
        int pauseTime;
        String name;

        public TimePrinter(int x, String n) {
                pauseTime = x;
                name = n;
        }

        public void run() {
                while (true) {
                        try {
                                System.out.println(name + ":"
                                                + new Date(System.currentTimeMillis()));
                                Thread.sleep(pauseTime);
                        } catch (Exception e) {
                                System.out.println(e);
                        }
                }
        }

        static public void main(String args[]) {
                TimePrinter tp1 = new TimePrinter(1000, "Fast Guy");
                tp1.start();
                TimePrinter tp2 = new TimePrinter(3000, "Slow Guy");
                tp2.start();
        }
}  



在本例中,我们可以看到一个简单的程序,它按两个不同的时间间隔(1 秒和 3 秒)在屏幕上显示当前时间。
这是通过创建两个新线程来完成的,包括 main() 共三个线程。
但是,因为有时要作为线程运行的类可能已经是某个类层次的一部分,所以就不能再按这种机制创建线程。
虽然在同一个类中可以实现任意数量的接口,但 Java 编程语言只允许一个类有一个父类。
同时,某些程序员避免从 Thread 类导出,因为它强加了类层次。对于这种情况,就要 runnable 接口。


2. 实现Runnable 接口
  此接口只有一个函数,run(),此函数必须由实现了此接口的类实现。但是,就运行这个类而论,其语义与前一个示例稍有不同。
我们可以用 runnable 接口改写前一个示例。(不同的部分用黑体表示。)
1.实现Runnable接口。
2.实现run()方法。
3.实例化新实现的类。
4.实例化由3.生成的实例作为参数的Tread类。
5.调用start()方法。


  创建两个新线程而不强加类层次

 import java.util.*;

class TimePrinterRunnable implements Runnable {
        int pauseTime;
        String name;

        public TimePrinterRunnable(int x, String n) {
                pauseTime = x;
                name = n;
        }

        public void run() {
                while (true) {
                        try {
                                System.out.println(name + ":"
                                                + new Date(System.currentTimeMillis()));
                                Thread.sleep(pauseTime);
                        } catch (Exception e) {
                                System.out.println(e);
                        }
                }
        }

        static public void main(String args[]) {
                Thread t1 = new Thread(new TimePrinterRunnable(1000, "Fast Guy"));
                t1.start();
                Thread t2 = new Thread(new TimePrinterRunnable(3000, "Slow Guy"));
                t2.start();
        }
}  



  请注意,当使用 runnable 接口时,您不能直接创建所需类的对象并运行它;必须从 Thread 类的一个实例内部运行它。许多程序员更喜欢 runnable 接口,因为从 Thread 类继承会强加类层次。


synchronized 关键字
  到目前为止,我们看到的示例都只是以非常简单的方式来利用线程。只有最小的数据流,而且不会出现两个线程访问同一个对象的情况。但是,在大多数有用的程序中,线程之间通常有信息流。试考虑一个金融应用程序,它有一个 Account 对象,如下例中所示:
  一个银行中的多项活动
Copy code
public class Account { 
 String holderName; 
 float amount; 
 public Account(String name, float amt) { 
  holderName = name; 
  amount = amt; 
 } 
 public void deposit(float amt) { 
  amount += amt; 
 } 
 public void withdraw(float amt) { 
  amount -= amt; 
 } 

 public float checkBalance() { 
  return amount; 
 } 
}
  在此代码样例中潜伏着一个错误。如果此类用于单线程应用程序,不会有任何问题。但是,在多线程应用程序的情况中,不同的线程就有可能同时访问同一个 Account 对象,比如说一个联合帐户的所有者在不同的 ATM 上同时进行访问。在这种情况下,存入和支出就可能以这样的方式发生:一个事务被另一个事务覆盖。这种情况将是灾难性的。但是,Java 编程语言提供了一种简单的机制来防止发生这种覆盖。每个对象在运行时都有一个关联的锁。这个锁可通过为方法添加关键字 synchronized 来获得。这样,修订过的 Account 对象(如下所示)将不会遭受像数据损坏这样的错误:
  对一个银行中的多项活动进行同步处理
Copy code

public class Account { 
 String holderName; 
 float amount; 

 public Account(String name, float amt) { 
  holderName = name; 
  amount = amt; 
 } 

 public synchronized void deposit(float amt) { 
  amount += amt; 
 } 

 public synchronized void withdraw(float amt) { 
  amount -= amt; 
 } 

 public float checkBalance() { 
  return amount; 
 } 
}
  deposit() 和 withdraw() 函数都需要这个锁来进行操作,所以当一个函数运行时,另一个函数就被阻塞。请注意, checkBalance() 未作更改,它严格是一个读函数。因为 checkBalance() 未作同步处理,所以任何其他方法都不会阻塞它,它也不会阻塞任何其他方法,不管那些方法是否进行了同步处理。


问题1.为什么不直接调用run()方法。
答:
   t1.run()只是方法调用,而不会启动一个新的线程,线程还是原来的那条;
   而t1.start()是在当前线程之外再启动一个新的线程。







    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多