您的位置:首页 > 编程语言 > Java开发

java多线程的使用

2016-09-11 12:21 211 查看

java多线程的简单使用

本文只是对java多线程知识做简单的介绍和使用。
 

一.操作系统中线程和进程的概念

现在的操作系统是多任务操作系统。多线程是实现多任务的一种方式。

 

(一)进程的理解
进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,

一个进程中可以启动多个线程。比如在Windows系统中,一个运行的exe就是一个进程。

 

(二)线程的理解

线程是指进程中的一个执行流程,一个进程中可以运行多个线程。

比如java.exe进程中可以运行很多线程。线程总是属于某个进程,进程中的多个线程共享进程的内存。

 

 (三)计算机的运行机制

计算机某一个时间点只能做一件事,这个时间点可以非常小,比如万分之一秒,之后又做别的事,

有时不断来回切换,我们看计算机同时在执行不同的任务,“同时”执行是人的感觉,

在线程之间实际上轮换执行。

 

二.Java中的线程

(一)线程的执行。

  使用java.lang.Thread类或者java.lang.Runnable接口编写代码来定义、实例化和启动新线程。

  一个Thread类实例只是一个对象,像Java中的任何其他对象一样,具有变量和方法,生死于堆上。

一个Java应用总是从main()方法开始运行,mian()方法运行在一个线程内,它被称为主线程。

  Java中,每个线程都有一个调用栈,即使不在程序中创建任何新的线程,线程也在后台运行着。  

一旦创建一个新的线程,就产生一个新的调用栈。

 

 (二)Java线程的创建

1.线程 定义

(1)扩展java.lang.Thread类。 

此类中有个run()方法,应该注意其用法:public void run()

如果该线程是使用独立的Runnable运行对象构造的,则调用该Runnable对象的run方法;

否则,该方法不执行任何操作并返回。 

Thread的子类应该重写该方法。

(2)实现java.lang.Runnable接口。 

public
void run()

使用实现接口Runnable的对象创建一个线程时,启动该线程将导致在独立执行的线程中调用对象的run方法。 

方法run的常规协定是,它可能执行任何所需的操作。

  有一点我们需要知道的是:多线程基本是用于执行耗时操作的功能,

比如:从网络上下载东西,或者从文件夹中读取东西等等,一般情况不用它。

而操作的实现都是放在run() 方法里面来来执行的。

 

(三)启动线程 

在线程的Thread对象上调用start()方法,而不是run()或者别的方法。 

在调用start()方法之前:线程处于新状态中,新状态指有一个Thread对象,但还没有一个真正的线程。 

注意:对Java来说,run()方法没有任何特别之处。像main()方法一样,它只是新线程知道调用的方法名称(和签名)。因此,在Runnable上或者Thread上调用run方法是合法的。但并不启动新的线程。

 

 

三.Java实例 

(一)实现Runnable接口的多线程例子

/**
* 实现Runnable接口的类
*/
public class DoSomething implements Runnable {
private String name;
public DoSomething(String name) {
this.name = name;
}
public void run() {//这是接口方法
for (int i = 0; i < 5; i++) {//线程内的操作!
for (long k = 0; k < 100000000; k++) ;
System.out.println(name + ": " + i);
}
}
}


/**
* 测试Runnable类实现的多线程程序
*
* @author leizhimin 2008-9-13 18:15:02
*/
publicclass TestRunnable {
public  static  void main(String[] args) {
DoSomething ds1 = new DoSomething("阿三");
DoSomething ds2 = new DoSomething("李四");

Thread t1 = new Thread(ds1);
Thread t2 = new Thread(ds2);

t1.start();
t2.start();
}
}


 

执行结果:

李四: 0
阿三: 0 
李四: 1 
阿三: 1 
李四: 2 
李四: 3 
阿三: 2 
李四: 4 
阿三: 3 
阿三: 4 

从结果可以看到,两个线程各自运行,互不影响。
 

(二)Thread类实现的多线程例子

 

/**
* 测试扩展Thread类实现的多线程程序
*/
public class TestThread extends Thread{

public TestThread(String name) {
super(name);//Thread类有一个参数的构造方法
}

publicvoid run() {
for(int i = 0;i<5;i++){
for(long k= 0; k <100000000;k++);
System.out.println(this.getName()+" :"+i);
}
}

public static void  main(String[] args) {
Thread t1 = new TestThread("阿三");
Thread t2 = new TestThread("李四");
t1.start();
t2.start();
}
}


 

执行结果:

阿三 :0
李四 :0 
阿三 :1 
李四 :1 
阿三 :2 
李四 :2 
阿三 :3 
阿三 :4 
李四 :3 
李四 :4 

多线程执行结果不一定每次是相同的。
 

对于上面的多线程程序代码来说,输出的结果是不确定的。

其中的一条语句for(long k= 0; k <100000000;k++);是用来模拟一个非常耗时的操作的。

 

 

五.线程状态

 (一)线程转换图

线程的状态转换是线程控制的基础。线程状态总的可分为五大状态:

分别是生、死、可运行、运行、等待/阻塞。用一个图来描述如下:



 

1.新状态:线程对象已经创建,还没有在其上调用start()方法。 

2.可运行状态:当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态。

当start()方法调用时,线程首先进入可运行状态。在线程运行之后或者从阻塞、等待或睡眠状态回来后,

也返回到可运行状态。

 

3.运行状态:线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。 

4.等待/阻塞/睡眠状态:这是线程有资格运行时它所处的状态。实际上这个三状态组合为一种,

其共同点是:线程仍旧是活的,但是当前没有条件运行。换句话说,它是可运行的,

但是如果某件事件出现,他可能返回到可运行状态。 

5.死亡态:当线程的run()方法完成时就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。

 

 

(二)阻止线程执行

对于线程的阻止,考虑一下三个方面,不考虑IO阻塞的情况:睡眠;等待;因为需要一个对象的锁定而被阻塞。

 

1.睡眠

Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)静态方法强制当前正在执行的线程休眠(暂停执行),以“减慢线程”。当线程睡眠时,它入睡在某个地方,在苏醒之前不会返回到可运行状态。当睡眠时间到期,则返回到可运行状态。 

线程睡眠的原因:线程执行太快,或者需要强制进入下一轮,因为Java规范不保证合理的轮换。 

睡眠的实现:调用静态方法。

        try {

            Thread.sleep(123);

        } catch (InterruptedException e) {

            e.printStackTrace();  

        }

 

睡眠的位置:为了让其他线程有机会执行,可以将Thread.sleep()的调用放线程run()之内。

这样才能保证该线程执行过程中会睡眠。 

例如,在前面的例子中,将一个耗时的操作改为睡眠,以减慢线程的执行。可以这么写:

 

    public void run() {

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

// 很耗时的操作,用来减慢线程的执行

//            for(long k= 0; k <100000000;k++);

            try {

                Thread.sleep(3);
            } catch (InterruptedException e) {

                e.printStackTrace();  .

            }

            System.out.println(this.getName()+" :"+i);

        }

    }

 

运行结果:

阿三 :0
李四 :0 
阿三 :1 
阿三 :2 
阿三 :3 
李四 :1 
李四 :2 
阿三 :4 
李四 :3 
李四 :4 

 

这样,线程在每次执行过程中,总会睡眠3毫秒,睡眠了,其他的线程就有机会执行了。

但是sleep不能保证3毫秒之后,睡眠的线程能马上醒过来。只能保证睡觉3毫秒,之后的运行情况由系统随机控制。

 

注意:

1、线程睡眠是帮助所有线程获得运行机会的最好方法。

2、线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。

3、sleep()是静态方法,只能控制当前正在运行的线程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: