黑马程序员-java学习之线程

来源:互联网 发布:公司域名申请 编辑:程序博客网 时间:2024/05/21 09:34

-------android培训java培训、期待与您交流! ----------

进程和线程:

进程是程序的一次动态执行过程,它对应了从代码加载、执行至执行完毕的一个完整过程,线程是比进程更小的单

位,一个进程执行过程中可以产生多个线程,每个线程有自身的产生、存在和消亡的过程,也是一个动态的概念。每

个进程都有一段专用的内存区域,而线程间可以共享相同的内存区域(包括代码和数据),并利用这些共享单元来实现

数据交换、实时通信与必要的同步操作。

每个Java程序都有一个默认的主线程。Java程序是从主类的main方法开始执行。当JVM加载代码,发现main方法

就启动一个线程,这个线程就称作"主线程",该线程负责执行main方法。在main方法中再创建的线程就是其他线程。

如果main方法中没有创建其他线程,那么当main方法返回时JVM就会结束Java应用程序。但如果main方法中创建了

其他线程,那么JVM就要在主线程和其他线程之间轮流切换,保证每个线程都有机会使用CPU资源,main方法返回

(主线程结束)JVM也不会结束,要一直等到该程序所有线程全部结束才结束Java程序(另外一种情况是:程序中调用了

Runtime类的exit方法,并且安全管理器允许退出操作发生。这时JVM也会结束该程序)。

在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口。

对于直接继承Thread的类来说,代码大致框架是:

class 类名 extends Thread{  方法1;  方法2;  …  public void run(){  // other code…  }  
属性1;  属性2;  …    }  

先看一个简单的例子:

/**   * @author Rollen-Holt 继承Thread类,直接调用run方法   * */ 
 
  class hello extends Thread { 
  
   
  private String name;
  public hello() {  
   }    
   public hello(String name) { 
        this.name = name;   
   }   
   public void run() {  
       for (int i = 0; i < 5; i++) {   
          System.out.println(name + "运行     " + i); 
       }  
    }   
  public static void main(String[] args) {   
   hello h1=new hello("A");  
   hello h2=new hello("B");  
   h1.run();  
   h2.run();   
 }     
 }  

【运行结果】:

A运行 0

A运行 1

A运行 2

A运行 3

A运行 4

B运行 0

B运行 1

B运行 2

B运行 3

B运行 4

我们会发现这些都是顺序执行的,说明我们的调用方法不对,应该调用的是start()方法。

当我们把上面的主函数修改为如下所示的时候:

public static void main(String[] args) {   
 hello h1=new hello("A");  
  hello h2=new hello("B");   
 h1.start();   
 h2.start();  
 }  


然后运行程序,输出的可能的结果如下:

A运行 0

B运行 0

B运行 1

B运行 2

B运行 3

B运行 4

A运行 1

A运行 2

A运行 3

A运行 4

因为需要用到CPU的资源,所以每次的运行结果基本是都不一样的,呵呵。

注意:虽然我们在这里调用的是start()方法,但是实际上调用的还是run()方法的主体

Java中实现多线程有两种方法:

1、继承Thread类,覆盖run()方法:使用Thread子类创建线程的优点是可以在子类中增加新的成员变量或方法,使线

程具有某种属性或功能。但Java不支持多继承,Thread类的子类不能再扩展其他的类。

2、实现Runnable接口:用Thread类直接创建线程对象,使用构造函数Thread(Runnable target)(参数target是一个

Runnable接口),创建线程对象时必须向构造方法参数传递一个实现Runnable接口类的实例,该实例对象称作所创线

程的目标对象。当线程调用start()方法,一旦轮到它使用CPU资源,目标对象自动调用接口中的run()方法(接口回

调)。线程间可以共享相同的内存单元(包括代码和数据),并利用这些共享单元来实现数据交换、实时通信与必要的同

步操作。对于Thread(Runnable target)创建的使用同一目标对象的线程,可以共享该目标对象的成员变量和方法。

另外,创建目标对象类在必要时还可以是某个特定类的子类,因此,使用Runnable接口比使用Thread的子类更具有

灵活性。(注意:具有相同目标对象的线程,run()方法中的局部变量相互独立,互不干扰)

在线程中启动其他线程,当线程调用start()方法启动,使之从新建态进入就绪队列,一旦得到CPU资源就脱离创建它

的主线程,开始自己的生命周期。

线程的常用方法:

start():线程调用该方法将启动线程,从新建态进入就绪队列,一旦享用CPU资源就可以脱离创建它的线程,独立开始

自己的生命周期。

run():Thread类的run()方法与Runnable接口中的run()方法功能和作用相同,都用来定义线程对象被调度后所进行的操

作,都是系统自动调用而用户不得引用的方法。run()方法执行完毕,线程就成死亡状态,即线程释放了分配给它的内

存(死亡态线程不能再调用start()方法)。在线程没有结束run()方法前,不能让线程再调用start()方法,否则将发生

IllegalThreadStateException异常。

sleep(int millsecond):有时,优先级高的线程需要优先级低的线程做一些工作来配合它,此时为让优先级高的线程让

出CPU资源,使得优先级低的线程有机会运行,可以使用sleep(int millsecond)方法。线程在休眠时被打断,JVM就抛

出InterruptedException异常。因此,必须在try-catch语句块中调用sleep方法。

isAlive():当线程调用start()方法并占有CPU资源后该线程的run()方法开始运行,在run()方法没有结束之前调用isAlive

()返回true,当线程处于新建态或死亡态时调用isAlive()返回false。

注意:一个已经运行的线程在没有进入死亡态时,不要再给它分配实体,由于线程只能引用最后分配的实体,先前的

实体就成为了"垃圾",并且不能被垃圾回收机制收集。

currentThread():是Thread类的类方法,可以用类名调用,返回当前正在使用CPU资源的线程。

interrupt():当线程调用sleep()方法处于休眠状态,一个占有CPU资源的线程可以让休眠的线程调用interrupt()方法"吵

醒"自己,即导致线程发生IllegalThreadStateException异常,从而结束休眠,重新排队等待CPU资源。

GUI线程:JVM在运行包含图形界面应用程序时,会自动启动更多线程,其中有两个重要的线程:AWT-EventQueue

和AWT-Windows。AWT-EventQueue线程负责处理GUI事件,AWT-Windows线程负责将窗体或组件绘制到桌面。

线程同步:(用synchronized修饰某个方法,该方法修改需要同步的变量;或用volatile修饰基本变量)

当两个或多个线程同时访问一个变量,并且一个线程需要修改这个变量时,应对这样的问题进行处理,否则可能发生

混乱。

要处理线程同步,可以把修改数据的方法用关键字synchronized修饰。一个方法使用synchronized修饰,当一个线程

A使用这个方法时,其他线程想使用该方法时就必须等待,直到线程A使用完该方法。所谓同步就是多个线程都需要

使用一个synchronized修饰的方法。

 
原创粉丝点击