十五,java线程

1.线程定义

通常来讲,咱们把正在计算机中执行的程序叫作“进程”(Process,而不将其称为程序(Program.所谓“线程”(Thread,是“进程”中某个单一顺序的控制流.新兴的操做系统,Mac,Windows,Linux,大多采用多线程的概念,把线程视为基本执行单位.线程也是Java中的至关重要的组成部分之一. java

最简单的Applet也是由多个线程来完成.Java,任何一个Appletpaint()update()方法都是由AWT(Abstract Window Toolkit)绘图与事件处理线程调用的,Applet 主要的方法:init(),start(),stop()destory() ——是由执行该Applet的应用调用的. 算法

某些地方用轻量进程(Lightweight Process)来代替线程,线程与真正进程的类似性在于它们都是单一顺序控制流.然而线程被认为轻量是因为它运行于整个程序的上下文内,能使用整个程序共有的资源和程序环境. 浏览器

做为单一顺序控制流,在运行的程序内线程必须拥有一些资源做为必要的开销.例如,必须有执行堆栈和程序计数器在线程内执行的代码只在它的上下文中起做用,所以某些地方用“执行上下文”来代替“线程”. 网络

2.线程属性

为了正确有效地使用线程,必须理解线程的各个方面并了解Java 实时系统.必须知道如何提供线程体、线程的生命周期、实时系统如何调度线程、线程组、什么是幽灵线程(Demon Thread. 多线程

2.1线程体

全部的操做都发生在线程体中,Java中线程体是从Thread类继承的run()方法,或实现Runnable接口的类中的run()方法.当线程产生并初始化后,实时系统调用它的run()方法.run()方法内的代码实现所产生线程的行为,它是线程的主要部分. 并发

2.2线程状态

详见线程生命周期. 函数

●新线程态(New Thread 学习

产生一个Thread对象就生成一个新线程.当线程处于“新线程”状态时,仅仅是一个空线程对象,它尚未分配到系统资源.所以只能启动或终止它.任何其余操做都会引起异常. 测试

●可运行态(Runnable spa

start()方法产生运行线程所必须的资源,调度线程执行,而且调用线程的run()方法.在这时线程处于可运行态.该状态不称为运行态是由于这时的线程并不老是一直占用处理机.特别是对于只有一个处理机的PC而言,任什么时候刻只能有一个处于可运行态的线程占用处理机.Java经过调度来实现多线程对处理机的共享.

●非运行态(Not Runnable

当如下事件发生时,线程进入非运行态.

suspend()方法被调用;

sleep()方法被调用;

③线程使用wait()来等待条件变量;

④线程处于I/O等待;

●死亡态(Dead

run()方法返回,或别的线程调用stop()方法,线程进入死亡态 .一般Applet使用它的stop()方法来终止它产生的全部线程.

2.3线程优先级

虽然咱们说线程是并发运行的.然而事实经常并不是如此.正如前面谈到的,当系统中只有一个CPU,以某种顺序在单CPU状况下执行多线程被称为调度(scheduling).Java采用的是一种简单、固定的调度法,即固定优先级调度.这种算法是根据处于可运行态线程的相对优先级来实行调度.当线程产生时,它继承原线程的优先级.在须要时可对优先级进行修改.在任什么时候刻,若是有多条线程等待运行, 系统选择优先级最高的可运行线程运行.只有当它中止、自动放弃、或因为某种缘由成为非运行态低优先级的线程才能运行.若是两个线程具备相同的优先级,它们将被交替地运行.

Java实时系统的线程调度算法仍是强制性的,在任什么时候刻,若是一个比其余线程优先级都高的线程的状态变为可运行态,实时系统将选择该线程来运行.

2.4幽灵线程

任何一个Java线程都能成为幽灵线程.它是做为运行于同一个进程内的对象和线程的服务提供者.例如,HotJava浏览器有一个称为“ 后台图片阅读器”的幽灵线程,它为须要图片的对象和线程从文件系统或网络读入图片.幽灵线程是应用中典型的独立线程.它为同一应用中的其余对象和线程提供服务.幽灵线程的run()方法通常都是无限循环,等待服务请求.

2.5线程组

每一个Java线程都是某个线程组的成员.线程组提供一种机制,使得多个线程集于一个对象内,能对它们实行总体操做.譬如,你能用一个方法调用来启动或挂起组内的全部线程.Java线程组由ThreadGroup类实现.当线程产生时,能够指定线程组或由实时系统将其放入某个缺省的线程组内.线程只能属于一个线程组,而且当线程产生后不能改变它所属的线程组.

3.线程的生命周期

3.1线程状态转换

Java,线程有5中不一样状态,分别是:新建(New)、就绪(Runable)、运行(Running)、阻塞(Blocked)和死亡(Dead.它们之间的转换图以下:

                                                         线程状态转换图

3.2线程生命周期

线程生命周期能够用下图表示:

一个线程的产生是从咱们调用了start方法开始进入Runnable状态,便可以被调度运行状态,并无真正开始运行,调度器能够将CPU分配给它,使线程进入Running状态,真正运行其中的程序代码.线程在运行时几个可能的去向:

1)执行时因调度器将CPU分配给了其它线程变为Runnable状态,等待被调度.

2)执行过程没有遇到任何阻隔,运行完成直接结束,也就是run()方法执行完毕.

3)执行过程当中请求锁,进入lock pool中等待对象的锁,等到会进入Runnable状态.

4)执行过程当中遇到wait()方法,被放入wait pool中等待,直到有notify()interrupt()方法被唤醒或打断进入lock pool等待对象锁,等到锁后进入Runnable状态.

推荐在run方法中使用控制循环条件的方式来结束一个线程.

  • wait:告诉当前线程放弃对象锁并进入等待状态,直到其余线程进入同一对象锁并调用notify为止.

  • notify:唤醒同一对象锁中调用wait的第一个线程.

  • notifyAll:唤醒同一对象锁中调用wait的全部线程,具备最高优先级的线程首先被唤醒并执行.

3.3 interrupt简述

interrupt()方法只是改变中断状态而已,它不会中断一个正在运行的线程.这一方法实际完成的是,给受阻塞的线程发出一个中断信号,这样受阻线程就得以退出阻塞的状态.更确切的说,若是线程被Object.wait, Thread.joinThread.sleep三种方法之一阻塞,此时调用该线程的interrupt()方法,那么该线程将抛出一个InterruptedException中断异常(该线程必须事先预备好处理此异常),从而提前地终结被阻塞状态.若是线程没有被阻塞,这时调用interrupt()将不起做用,直到执行到wait(),sleep(),join(),才立刻会抛出 InterruptedException.

线程A在执行sleep,wait,join,线程B调用线程Ainterrupt方法,的确这一个时候A会有InterruptedException 异常抛出来.但这实际上是在sleep,wait,join这些方法内部会不断检查中断状态的值,而本身抛出的InterruptedException.若是线程A正在执行一些指定的操做时如值,for,while,if,调用方法等,不会去检查中断状态,则线程A不会抛出 InterruptedException,而会一直执行着本身的操做.

注意:
①当线程A执行到wait(),sleep(),join(),抛出InterruptedException,中断状态已经被系统复位了,线程A调用Thread.interrupted()返回的是false.
②若是线程被调用了interrupt(),此时该线程并不在wait(),sleep(),join(),下次执行wait(),sleep(),join(),同样会抛出InterruptedException,固然抛出后该线程的中断状态也回被系统复位.

几种比较:

1. sleep() &interrupt()
线程A正在使用sleep()暂停着: Thread.sleep(100000),若是要取消它的等待状态,能够在正在执行的线程里(好比这里是B)调用a.interrupt(),令线程A放弃睡眠操做,这里a是线程A对应到的Thread实例.
当在sleep中时线程被调用interrupt(),就立刻会放弃暂停的状态并抛出InterruptedException.抛出异常的,A线程.

2. wait() &interrupt()
线程A调用了wait()进入了等待状态,也能够用interrupt()取消.不过这时候要注意锁定的问题.线程在进入等待区,会把锁定解除,当对等待中的线程调用interrupt(),先从新获取锁定,再抛出异常.在获取锁定以前,是没法抛出异常的.

3. join() &interrupt()
当线程以join()等待其余线程结束时,当它被调用interrupt(),它与sleep()时同样,会立刻跳到catch块里.

注意:是对谁调用interrupt()方法,必定是调用被阻塞线程的interrupt方法.

4.多线程的实现

4.1继承Thread

使用格式:

class MyThread extends Thread {
	public void run() {
		// 定义一个类继承Thread,覆写run方法,这里写上线程的内容
	}
	public static void main(String[] args) {
             // 实例调用start方法启动一个线程
          new MyThread().start();
    }
}

示例:

class ThreadTest extends Thread {
	private int ticket = 100;
	public void run() {
		while (true) {
			if (ticket > 0) {
				System.out.println(Thread.currentThread().getName()
						+ "is saling ticket" + ticket--);
			} else {
				break;
			}
		}
	}
}
main测试类 :
public class ThreadDome1 {
	public static void main(String[] args) {
		ThreadTest t = new ThreadTest();
		t.start();
		t.start();
		t.start();
		t.start();
	}
}
上面的代码中 ,咱们用 ThreadTest类模拟售票处的售票过程 ,run方法中的每一次循环都将总票数减 1,模拟卖出一张车票 ,同时该车票号打印出来 ,直接剩余的票数到零为止 .ThreadDemo1类的 main方法中 ,咱们建立了一个线程对象 ,并重复启动四次 ,但愿经过这种方式产生四个线程 .从运行的结果来看咱们发现其实只有一个线程在运行 ,这个结果告诉咱们: 一个线程对象只能启动一个线程 ,不管你调用多少遍 start()方法 ,结果只有一个线程 . 运行会报 java.lang.IllegalThreadStateException异常 .

咱们接着修改ThreadDemo1,main方法中建立四个Thread对象:

示例:

public class ThreadDemo1 {
	public static void main(String[] args) {
		new ThreadTest().start();
		new ThreadTest().start();
		new ThreadTest().start();
		new ThreadTest().start();
	}
}

示例:

class ThreadTest extends Thread {
	private int ticket = 100;
	public void run() {
		while (true) {
			if (ticket > 0) {
				System.out.println(Thread.currentThread().getName()
						+ " is saling ticket" + ticket--);
			} else {
				break;
			}
		}
	}
}
从结果上看每一个票号都被打印了四次 ,即四个线程各自卖各自的 100张票 ,而不去卖共同的 100张票 .

咱们须要的是,多个线程去处理同一个资源,一个资源只能对应一个对象,在上面的程序中,咱们建立了四个ThreadTest对象,就等于建立了四个资源,每一个资源都有100张票,每一个线程都在独自处理各自的资源.

总结:要实现这个铁路售票程序,咱们只能建立一个资源对象,但要建立多个线程去处理同一个资源对象,而且每一个线程上所运行的是相同的程序代码.

所以能够考虑使用接口编写多线程.


4.2实现Runnable接口

使用格式:

class MyThread implements Runnable {
	public void run() {
		// 这里写上线程的内容
	}
	public static void main(String[] args) {
		// 使用这个方法启动一个线程
		new Thread(new MyThread()).start();
	}
    //将实现接口的示例做为Thread实例的参数传入,调用start()方法.
}

示例:

class ThreadTest implements Runnable {
	private int tickets = 100;

	public void run() {
		while (true) {
			if (tickets > 0) {
				System.out.println(Thread.currentThread().getName()
						+ " is saling ticket " + tickets--);
			}
		}
	}
}
示例 :
public class ThreadDemo1 {
	public static void main(String[] args) {
		ThreadTest t = new ThreadTest();
		new Thread(t).start();
 
		new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
	}
}

上面的程序中 ,建立了四个线程 ,每一个线程调用的是同一个 ThreadTest对象中的 run()方法 ,访问的是同一个对象中的变量( tickets)的实例 ,这个程序知足了咱们的需求 .


4.3两种方式总结

java全部实现Runnable接口的类均可被启动一个新线程,新线程会执行该实例的run()方法,run()方法执行完毕后,线程就结束了.一旦一个线程执行完毕,这个实例就不能再从新启动,只能从新生成一个新实例,再启动一个新线程.

Thread类是实现了Runnable接口的一个实例,它表明一个线程的实例,而且,启动线程的惟一方法就是经过Thread类的start()实例方法.

start()方法是一个native方法,它将启动一个新线程,并执行run()方法.Thread类默认的run()方法什么也不作就退出了.注意:直接调用run()方法并不会启动一个新线程,它和调用一个普通的java方法没有什么区别. 所以,有两个方法能够实现本身的线程:

方法1:本身的类extend Thread,并复写run()方法,就能够启动新线程并执行本身定义的run()方法.

方法2:若是本身的类已经extends另外一个类,必须经过实现一个Runnable接口实现.

事实上,当传入一个Runnable target参数给Thread,Threadrun()方法就会调用target.run(),参考JDK源代码:

public void run() {
    if (target != null) {
        target.run();
    }
}
实现 Runnable接口相对于继承 Thread类来讲 ,有以下显著的好处:

①适合多个相同程序代码的线程去处理同一资源的状况,把虚拟CPU(线程)同程序的代码,数据有效的分离,较好地体现了面向对象的设计思想.
②能够避免因为Java的单继承特性带来的局限.
③有利于程序的健壮性,代码可以被多个线程共享,代码与数据是独立的.当多个线程的执行代码来自同一个类的实例时,即称它们共享相同的代码.多个线程操做相同的数据,与它们的代码无关.当共享访问相同的对象是,即它们共享相同的数据.当线程被构造时,须要的代码和数据经过一个对象做为构造函数实参传递进去,这个对象就是一个实现了Runnable接口的类的实例.



20150419


JAVA学习笔记系列

--------------------------------------------

                    联系方式

--------------------------------------------

        Weibo: ARESXIONG

        E-Mail: aresxdy@gmail.com

------------------------------------------------
相关文章
相关标签/搜索