java多线程(二):线程的实现

来源:互联网 发布:php网店 编辑:程序博客网 时间:2024/06/06 08:48

二、线程的实现

Java中实现多线程代码有两种方式,其一:继承Thread类,其二:实现Runnable接口。下面将介绍这两中方式。

1、实现(创建与启动)多线程方式之继承Thread

  格式: class 类名称 extends Thread {

            属性......

            方法......

            Public void run(){

             ...线程主体...

             }

        }

  要点: 1、子类要复写父类中的run方法,将线程运行的代码存放在run中。

       2、建立子类对象的同时线程也被创建。

       3、通过调用start方法开启线程。

  示例

       Class myThread extends Thread {

            Long myname ;

            myThread(long myname){

                 This.myname=myname; // 通过构造方法设置属性内容

            }

       }

       Public void run(){

           ...线程主体...

       }

 

       然后,下列代码会创建并启动一个线程

       myThread p = new pThread (xxx);

       P.start();

2、实现(创建与启动)多线程方式之实现Runnable接口

  格式: class 类名称 implements Runnable {

            属性......

            方法......

            Public void run(){

             ...线程主体...

             }

        }

  要点1子类覆盖接口中的run方法。

        2、通过Thread类创建线程,并将实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数。                  

        3Thread类对象调用start方法开启线程。

  示例 

      class myRun extends Runnable{

            long myname ;

            myRun(long myname){

                 This.myname=myname; // 通过构造方法设置属性内容

            }

       }

       public void run(){

           ...线程主体...

       }

       然后,下列代码会创建并启动一个线程。

       myRun p = new myRun(xxx);

       new Thread(p).strart();//注意这个地方下面将会说明

这里我们需要进行说明一下:

       以上代码通过Runnable接口实现多线程,但这样一来就会有新的问题产生,从之前代码中可以清楚地知道,要想启动一个多线程必须要使用strart()方法完成,如果继承了Thread类,则可以直接从Thread类中使用start()方法,但是现在实现的是Runnable接口,那该如何启动多线程呢?实际上,此时还是要依靠Thread类完成启动,在Thread类中提供了public Thread(Runnable target)public Thread(Runnable target, String name)两个构造方法。这两个构造方法都可以接受Runnable的子类实例对象,所以就可以依靠此点启动多线程,具体代码就是上面new Thread(p).strart();

 

3售票例子:

50张票需要3个窗口卖出。用两种开启线程方式买票,观察两种方式买票的结果有什么不同?(每个窗口就是一个线程)

在这里要说明两个方法:

currentThread():返回对当前正在执行的线程对象的引用。

getName():获取线程名称。setName()设置线程名字;

方式一:之继承Thread

class Ticket1 extends Thread {

int num = 3;

public Ticked1 (String name) {

super(name);

} // 是为主函数中new Ticket1(线程X)写的构造方法

public void run() {

for (int i = 0; i <= 30; i++) {

if (num > 0){

         System.out.println(getName() + "卖出第" + num-- +"张票");

           }

      }

  }

}

public class TicketDemo{

public static void main(String[] args){

//这里开三个窗口,相当于三个线程

new Ticket1(A窗口).start();

New Ticket1(B窗口).start();

New Ticket1(C窗口).start();

//new Ticket1(A窗口).start();相当于:

//Ticket1 A = new Thread;定义线程对象A

//A.start();             启动第一个线程A

}

}

上述程序的运行结果如下:

窗口B卖出第:1张票

窗口A卖出第:1张票

窗口C卖出第:1张票

窗口A卖出第:2张票

窗口B卖出第:2张票

窗口C卖出第:2张票

窗口C卖出第:3张票

窗口B卖出第:3张票

窗口A卖出第:3张票

注:结果显示可能顺序不同

易见上述三个窗口分别卖出了3张票,这如果在现实中显然是无法接受!!!

 

方式二:实现Runnable接口

class Ticket2 implements Thread {

int num = 3;

public void run() {

for (int i = 0; i <= 30; i++) {

if (num > 0){

         System.out.println(Thread.currentThread().getName() + "卖出第" + num-- +"张票");

        // currentThread():返回对当前正在执行的线程对象的引用。

        // getName():返回线程名称。  

          }

     }

  }

}

public class TicketDemo{

public static void main(String[] args){

//这里开三个窗口,相当于三个线程

Runnable target = new Ticked2();

new Thread(target,A).start();

new Thread(target,B).start();

new Thread(target,C).start();

// 构造方法public Thread(Runnable target, String name)

// 分配新的Thread对象

}

}

上述程序的运行结果如下:

窗口B卖出第:1张票

窗口A卖出第:2张票

窗口C卖出第:3张票

注:结果显示可能顺序不同

可见上述三个窗口总共卖出了3张票

分析上述两种方法,可见,实现Runnable接口相对继承Thread类来说,有如下显著优势

1、多线程共享一个目标资源,适合多线程处理同一份资源。

2、该类还可以继承其他类,也可以实现其他接口。

 

一些常见问题 @熔岩

1、线程的名字,一个运行中的线程总是有名字的,名字有两个来源,一个是虚拟机自己给的名字,一个是你自己的定的名字。在没有指定线程名字的情况下,虚拟机总会为线程指定名字,并且主线程的名字总是mian,非主线程的名字不确定。

2、线程都可以设置名字,也可以获取线程的名字,连主线程也不例外。

3、获取当前线程的对象的方法是:Thread.currentThread()

4、在上面的代码中,只能保证:每个线程都将启动,每个线程都将运行直到完成。一系列线程以某种顺序启动并不意味着将按该顺序执行。对于任何一组启动的线程来说,调度程序不能保证其执行次序,持续时间也无法保证。

5、当线程目标run()方法结束时该线程完成。

6、一旦线程启动,它就永远不能再重新启动。只有一个新的线程可以被启动,并且只能一次。一个可运行的线程或死线程可以被重新启动。

7、线程的调度是JVM的一部分,在一个CPU的机器上上,实际上一次只能运行一个线程。一次只有一个线程栈执行。JVM线程调度程序决定实际运行哪个处于可运行状态的线程。

众多可运行线程中的某一个会被选中做为当前线程。可运行线程被选择运行的顺序是没有保障的。

8、尽管通常采用队列形式,但这是没有保障的。队列形式是指当一个线程完成“一轮”时,它移到可运行队列的尾部等待,直到它最终排队到该队列的前端为止,它才能被再次选中。事实上,我们把它称为可运行池而不是一个可运行队列,目的是帮助认识线程并不都是以某种有保障的顺序排列唱呢个一个队列的事实。

9、尽管我们没有无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式。

0 0