-------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修饰的方法。