day_34Java多线程1

星期五, 十二月 04, 2015 19:40:55

一、进程和线程

采用Java的多线程机制可以使计算机资源得到更充分的使用,多线程可以使程序在同一时间内完成很多操作。

       本章讲解 进程与线程的共同点和区别、实现多线程的方法、线程的状态、对线程操作的方法、多线程的同步、线程间的通信,

以及线程生命周期的控制等内容。

        本章要点:

              了解进程和线程

               掌握实现多线程的方法

               熟悉线程的状态

               熟悉线程操作的方法

               熟悉线程同步的方法

               熟悉线程间通信的方法

      Java是少数的几种支持“多线程”的语言之一。

     大多数的程序语言只能循序运行单独的一个程序块,但无法同时运行不同的多个程序块。

     

          Java的“多线程”恰可弥补这个遗憾,它可以让不同的程序块一起运行,

   如此一来就可让程序运行的更为顺畅,同时也可达到多任务处理的目的。

   进程的特征是:

           1.一个进程就是一个执行中的程序,而每一个进程都有自己独立的一块内存空间,一组系统资源。

     在进行概念中,每一个进程的内部数据和状态都是完全独立的。

           2.创建并执行一个进程的系统开销比较大。

           3.进程是程序的一次执行过程,是系统运行程序的基本单位。

     

   线程的特征是:

   

         1.在java中,程序通过流控制来执行程序流。程序中单个顺序的流控制称为线程。

         2.多线程指的是在单个进行中可以同时运行多个不同的线程,执行不同的任务。

多线程意味着一个程序的多行语句可以看上去几乎同时运行。

    二者都是一段完成某个特定功能的代码,是程序中单个顺序的流控制。

         不同的是同类的多个线程是共享一块内存空间和一组系统资源,

而线程本身的数据通常只有微处理器的寄存器数据,以及一个供程序执行时使用的堆栈。

所以系统在产生一个线程,或者在各个线程之间切换时,负担要比进程小得多,正因为如此,线程也被

称为轻负荷进程。一个进程中可以包含多个线程。

      多线程是实现并发机制的一种有效手段。

进程和线程一样,都是实现并发的一个基本单位。

线程和进程的只要差别体现在以下两个方面。

  1.同样作为基本的执行单元,线程是划分得比进程更小的执行单位。

  2.每个进程都有一段专用的内存区域。与此相反,线程却共享内存单元(包括代码和数据),

通过共享的内存单元来实现数据交换、实时通信与必要的同步操作。

         多线程的应用范围很广。在一般情况下,程序的某些部分同待定的事件或资源联系在一起,同时又不想为它而

暂停程序其他部分的执行,在这种情况下,就可以考虑创建一个线程,令它与那个事件或资源关联到一起,并让它

独立于主程序运行。

通过使用线程,可以避免用户在运行程序和得到结果之间的停顿,还可以让一些任务(如打印任务)在后台运行,

而用户则在前台继续完成一些其他的工作。

      总之,利用多线程技术,可以使编程人员方便的开发出能同时处理多个任务的功能强大的应用程序。

二、认识线程

     

    2.1.所谓线程(Thread)是指程序的运行流程,“多线程”的机制则是指可以同时运行多个程序块,使程序运行的效率变得更高,

也可克服传统程序语言所无法解决的问题。

   例如,有些包含循环的线程可能要使用比较长的一段时间来运行,此时便可让另一个线程来做其他的处理。

   2.2本节用一个程序来说明单一线程与多线程的不同。

      Demo1是单一线程的范例。

   
       2.2.2代码案例1

package day34;

public class ThreadDemo1 {
	//单一线程,只有等Thread运行完才可以运行main()
	public static void main(String[] args) {
		Thread td = new Thread();
		td.run();
		System.out.print("\n");
		for(int i=0;i<5;i++) {
			System.out.print("main()..."+i+"\t");
		}
	}
}

class Thread {
	public void run() {
		for(int i = 0;i<5;i++) {
			System.out.print("Thread ..."+i+"\t");
		}
	}
}

 运行结果:

Thread ...0Thread ...1Thread ...2Thread ...3Thread ...4

main()...0main()...1main()...2main()...3main()...4

2.2.2代码案例2

    在java里,是否可以同时运行,交替输出。其方法是--在java里激活多线程。

    

    如何激活线程:

   如果在类里要激活线程,必须先做好下面的两个准备

       1.线程必须扩展自Thread类,使自己成为它的子类。

       2.线程的处理必须编写在run()方法内。

2.3通过继承Thread类实现多线程

   Thread存放在java.lang类库中。run()方法是定义在Thread类里的一个方法,因此把线程的程序代码编写在run()方法内,

事实上所做的就是覆写的操作。

   因此要使一个类可激活线程,必须按照下面的格式:

     class 类名称 extends Thread{ //从Thread类扩展出子类

       属性...

       方法...

       修饰符 run() {             //覆写Thread类里的run()方法

         以线程处理的程序;

       }

     }

2.3.1代码案例

package day34;
import java.lang.Thread;

public class ThreadDemo2 {
	public static void main(String[] args) {
		/*使用线程最大的问题就是不可确定性。
		 * 线程运行是,谁先拿到锁,谁先运行,没先后之分的。*/
		Thread2 td2 = new Thread2();
		td2.start();
		
		for(int i=0;i<3;i++) {
			System.out.print("main..."+i+"\t");
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
/*如果在类里要激活线程,必须先做好下面的两个准备
 *1.线程必须扩展自Thread类,使自己成为它的子类。
 *2.线程的处理必须编写在run()方法内。*/
class Thread2 extends Thread{
	//java.lang.Thread
	@Override
	public void run() {
		for(int i =0;i<3;i++){
			System.out.print("Thread2..."+i+"\t");
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
}


运行结果:
main...0	Thread2...0	Thread2...1	main...1	Thread2...2	main...2	

 注意:

    1.就不应该是交替显示,cpu分配给各个线程的执行时间是不确定的,另外每个线程也有优先级别

    2.使用线程最大的问题就是不可确定性。线程运行是,谁先拿到锁,谁先运行,没先后之分的。

2.4 通过实现Runnable接口实现多线程

    在java中如果一个类继承了某一个累 ,同时又想采用多线程技术的时候,就不能用Thread类产生线程,

因为java不允许多继承,这时要用Runnable接口来实现创建线程。

    格式:

     class 类名称 implements Runnable { //实现Runnable接口

        属性...

        名称...

        修饰符 run(){      //覆写Thread类里的run()方法

        以线程处理的程序;  

        }

     }

2.4.1用Runnable接口实现多线程使用实例

代码案例:

package day34;

public class ThreadRunnable {
	public static void main(String[] args){
		Thread3 td3 = new Thread3();
		@SuppressWarnings("unused")
		/*Thread(Runnable target) 分配新的 Thread 对象。
		 * 因为Runnable接口没start()方法,所以要通过Thread来启动多线程
		 */
		Thread td4 = new Thread(td3);
		td4.start();
		
		for(int i =0;i<3;i++) {
			System.out.print("main..."+i+"\t");
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}


//
class Thread3 implements Runnable {

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for (int i =0 ;i<3;i++) {
			System.out.print("Thread3..."+i+"\t");
		}
	}
	
}

运行结果:

main...0Thread3...0Thread3...1Thread3...2main...1main...2

注意:

    1.将一个Runnable接口的实例化对象作为参数去实例化Thread类对象。

    2.在实际的开发中,尽可能地使用Runnable接口去实现多线程机制。

三、两种多线程实现机制的比较

1.java.lang.Thread 所已实现的接口: Runnable

2.区别:

  通过编写一个应用程序来比较分析。

3.代码案例

模拟铁路售票系统的范例,实现4个售票发售某日某次列车的车票20张,一个售票点用一个线程来表示。

星期日, 十二月 06, 2015   14:17:38

星期日, 十二月 06, 2015  16:17:08

首先用继承Thread类来实现这个程序。

currentThread() 

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

3.1代码案例:

package day34;

public class ThreadDemo4 {
	public static void main(String[] args) {
		/*模拟铁路售票系统的范例,实现4个售票发售某日某次列车的车票20张,
		一个售票点用一个线程来表示。*/
		Thread5 th5 = new Thread5();
		//一个线程对象只能启动一次
		th5.start();
		th5.start();
		th5.start();
		th5.start();
	}

}
class Thread5 extends Thread {
	private int tickets = 20;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			if(tickets>0) {
			System.out.println(Thread.currentThread().getName()+"..."+(tickets--));
			}
		}
	}
}

 运行结果:

Thread-0...20

Thread-0...19

Thread-0...18

Thread-0...17

Thread-0...16

Thread-0...15

Thread-0...14

Thread-0...13

Thread-0...12

Thread-0...11

Thread-0...10

Thread-0...9

Thread-0...8

Thread-0...7

Thread-0...6

Thread-0...5

Thread-0...4

Thread-0...3

Thread-0...2

Thread-0...1

Exception in thread "main" java.lang.IllegalThreadStateException

at java.lang.Thread.start(Thread.java:595)

at day34.ThreadDemo4.main(ThreadDemo4.java:10)

注意:

    1.创建了TestThrea类的实例化对象,之后调用了4此start()方法。

但从运行的结果可知,程序运行时出现了异常,之后却只有一个线程在运行。

     2.这 说明了一个类继承Thread类之后,这个类的对象无论调用多少次start()方法,结果只有一个线程在运行。

     3.Thread.currentThread().getName()取得当前运行的线程名称。

3.2代码案例:

   启动4个线程,分别进行各自的操作

package day34;

public class ThreadDemo5 {
	public static void main(String[] args) {
		/*模拟铁路售票系统的范例,实现4个售票发售某日某次列车的车票20张,
		一个售票点用一个线程来表示。*/
		
		//启动4个线程,分别进行各自的操作
		new Thread6().start();
		new Thread6().start();
		new Thread6().start();
		new Thread6().start();
		
	}

}
class Thread6 extends Thread {
	private int tickets = 20;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			if(tickets>0) {
			System.out.println(Thread.currentThread().getName()+"..."+(tickets--));
			}
		}
	}
}

 运行结果:

Thread-0...20

Thread-0...19

Thread-0...18

Thread-2...20

Thread-0...17

Thread-0...16

Thread-0...15

Thread-0...14

Thread-0...13

Thread-0...12

Thread-3...20

Thread-2...19

................

注意:

     1.启动4个线程对象,但这4个线程对象各自占有各自的资源,所以:用Thread类实际上无法达到资源共享的目的。

3.3代码案例: 用Runnable接口实现多线程使用

package day34;

public class ThreadDemo6 {
	public static void main(String[] args) {
		/*模拟铁路售票系统的范例,实现4个售票发售某日某次列车的车票20张,
		一个售票点用一个线程来表示。*/
		Thread6 td6 = new Thread6();
		//启动4个线程,并实现了资源共享的目的
		new Thread(td6).start();
		new Thread(td6).start();
		new Thread(td6).start();
		new Thread(td6).start();
		
	}

}
class Thread6 implements Runnable {
	private int tickets = 20;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			if(tickets>0) {
			System.out.println(Thread.currentThread().getName()+"..."+(tickets--));
			}
		}
	}
}

 运行结果:

Thread-2...20

Thread-3...19

Thread-2...18

Thread-3...17

Thread-2...16

Thread-3...15

Thread-2...14

Thread-3...13

Thread-2...12

Thread-3...11

Thread-2...10

Thread-3...9

Thread-2...8

Thread-3...7

Thread-2...6

Thread-3...5

Thread-2...4

Thread-3...3

Thread-2...2

Thread-3...1

注意:

    1.启动4个线程,但结果都是操纵同一资源,从而实现了资源共享的目的。

3.4总结

实现Runnable接口相对于继承Thread类来说,有以下优势:

    1.适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码、数据有效分离,

较好的体现了面向对象的设计思想。

    2.可以避免由于java的单继承特性带来的局限。

开发中经常会碰到这样一种情况,即要将已经继承了某一个子类放入多线程中,由于一个类不能同时有两个父类,

所以不能使用继承Thread类的方式,那么就只能采用实现Runnable接口的方式。

   3.增强了程序的健壮性,代码能够被多个线程共享,代码与数据时独立的。

当多个线程的执行代码来自同一个类的实例时,即称它们共享相同的代码。多个线程可以操作相同的数据,与它们的代码无关。

当共享访问相同的对象时,即共享相同的数据。当线程被构造时,需要的代码和数据通过一个对象作为构造方法实参传递进去,

这个对象就是一个实现了Runnable接口的类和实例。

   事实上,几乎所有的多线程应用都应用实现Runnable接口。

星期日, 十二月 06, 2015 17:35:10

猜你喜欢

转载自yuzhouxiner.iteye.com/blog/2262153