河北神马推广多少钱:多线程/JAVA多线程 - 魔乐培训 高端JAVA培训 魔乐科技JAVA培训

来源:百度文库 编辑:中财网 时间:2024/05/06 05:12:05

多线程

 发布日期:2008-10-30 8:57:00 发布者:[IT电子教育门户]   评论:[ 8]  浏览: 850

1、课程名称:多线程
 多线程的基本作用、两种实现的区别、同步与死锁的概念。
 在开发中真正去编写多线程代码是很少的,要的只是基本概念,本章就是基本概念。
2、知识点
2.1、上次课程的主要知识点
 1、 包的定义及使用
 2、 四种访问控制权限
2.2、本次预计讲解的知识点
 1、 多线程的概念
 2、 多线程的两种实现方式及区别
 3、 多线程的主要操作方法
 4、 同步与死锁的概念
3、具体内容
3.1、多线程的基本概念
 JAVA是少数几个支持多线程的语言,所以多线程也就是成为了JAVA的一个特点。
 最早的DOS系统一旦有病毒进入,则立刻死机,因为在同一个时间段上,永远只能有一个进程执行。
 而windows就算是有病毒产生了,则系统照样可以执行。因为是属于多进程的操作系统。
 进程:每一个应用程序的启动就称为进程。
 线程:是在进程的基础之上划分出来的比进程更小的时间单位。
 线程的运行速度要超过进程。
 一个操作系统虽然可以跑多个进程,但是对于整个电脑而言只有一个CPU,所以在同一个时间段上CPU会处理多个程序,而在同一个时间点上CPU只会处理一个程序。
3.2、线程的实现
 在JAVA中要想实现一个多线程的程序有两种方式:
  • 继承Thread类
  • 实现Runnable接口
3.2.1、继承Thread类实现
 一个类只要继承了Thread类,那么此类就称为多线程的操作类。
例如:以下代码
class MyThread extends Thread{
};
 线程在操作之中应该存在线程的主体,线程的主体方法为:run方法,所以Thread的子类必须覆写run方法。
例如:以下代码覆写了run方法
class MyThread extends Thread{
 private String name ;
 public MyThread(String name){
  this.name = name ;
 }
 public void run(){
  for(int i=0;i<100;i++){
   System.out.println(name + "运行,i = " + i) ;
  }
 }
};
public class ThreadDemo01{
 public static void main(String args[]){
  MyThread mt1 = new MyThread("线程A ") ;
  MyThread mt2 = new MyThread("线程B ") ;
  mt1.run() ;
  mt2.run() ;
 }
};
 编译并运行程序,观察效果。从运行结果可以发现,是A执行完之后再执行B,但是之前的程序并没有同时运行,因为此时,线程并没有启动,如果要想启动线程则必须调用Thread类中的start()方法才可以。例如:修改之前的代码:
class MyThread extends Thread{
 private String name ;
 public MyThread(String name){
  this.name = name ;
 }
 public void run(){
  for(int i=0;i<100;i++){
   System.out.println(name + "运行,i = " + i) ;
  }
 }
};
public class ThreadDemo02{
 public static void main(String args[]){
  MyThread mt1 = new MyThread("线程A ") ;
  MyThread mt2 = new MyThread("线程B ") ;
  mt1.start() ;
  mt2.start() ;
 }
};
 发现此时的运行结果是,两个线程之间交替执行。
问题?
 既然start()方法调用的还是run方法,那为什么不直接调用run方法呢?
 观察JDK中的Thread类实现
start()方法的定义:
    public synchronized void start() {
        if (started)
            throw new IllegalThreadStateException();
        started = true;
        start0();
    }
    private native void start0();
 如果一个线程已经启动了之后再启动第二次,则会报非法的线程状态。
 在JAVA中有一种技术称为:JNI技术,表示的是JAVA本地接口,可以通过JAVA调用本地操作系统中的函数支持。所以线程的实现靠的是操作系统的支持。
 如果一个多线程的操作,使用了Thread类来完成的话,那么此类就不能继承其他的类了。
3.2.2、实现Runnable接口
 实现Rrnnable接口同时覆写run方法
class MyThread implements Runnable{
 private String name ;
 public MyThread(String name){
  this.name = name ;
 }
 public void run(){
  for(int i=0;i<100;i++){
   System.out.println(name + "运行,i = " + i) ;
  }
 }
};
 但是Runnable接口与Thread类不同的是,Runnable接口中并没有关于start()方法的定义,而只有Thread类中才有。继续观察Thread类。
 观察Thread类中有如下两个构造方法:
  • public Thread(Runnable target)
• public Thread(Runnable target,String name)
例如:使用以上的操作启动多线程
class MyThread implements Runnable{
 private String name ;
 public MyThread(String name){
  this.name = name ;
 }
 public void run(){
  for(int i=0;i<100;i++){
   System.out.println(name + "运行,i = " + i) ;
  }
 }
};
public class ThreadDemo03{
 public static void main(String args[]){
  MyThread mt1 = new MyThread("线程A ") ;
  MyThread mt2 = new MyThread("线程B ") ;
  new Thread(new MyThread("线程A")).start() ;
  new Thread(new MyThread("线程B")).start() ;
 }
};
3.2.3、Thread类与Runnable接口的关系
 1、 Thread也是Runnable接口的子类
  • public class Thread extends Object implements Runnable
 2、 Thread类中有单继承的局限,而Runnable中并没有此局限
 3、 使用Runnable实现的多线程操作类,适合于多个线程对象共享数据
例如:验证资源的共享
A、 使用Thread类完成
class MyThread extends Thread{
 private int ticket = 10 ;
 public void run(){
  for(int i=0;i<100;i++){
   if(ticket>0){
    System.out.println("卖票。ticket = " + ticket--) ;
   }
  }
 }
};
public class ThreadDemo04{
 public static void main(String args[]){
  MyThread mt1 = new MyThread() ;
  MyThread mt2 = new MyThread() ;
  MyThread mt3 = new MyThread() ;
  mt1.start() ;
  mt2.start() ;
  mt3.start() ;
 }
};
 整个程序一共卖出了30张票,但是实际上只有10张票,这是因为每一个线程对象都拥有各自的10张票。而如果现在使用Runnable接口实现的话,则可以达到资源的共享
B、 使用Runnable接口实现
class MyThread implements Runnable{
 private int ticket = 10 ;
 public void run(){
  for(int i=0;i<100;i++){
   if(ticket>0){
    System.out.println("卖票。ticket = " + ticket--) ;
   }
  }
 }
};
public class ThreadDemo05{
 public static void main(String args[]){
  MyThread mt = new MyThread() ;
  new Thread(mt).start() ;
  new Thread(mt).start() ;
  new Thread(mt).start() ;
 }
};
3.3、线程的状态
 
 所有的线程虽然在语句上有先有后,但是是同时启动的,谁先抢占到CPU资源,谁就运行。
 线程创建完成之后,等待启动,启动之前先进入到就绪状态。之后等待CPU调度,调度之后进入运行状态。
 如果中间有问题了,导致程序暂停执行,则进入到阻塞状态。回到就绪状态,等待再次执行。
 当全部程序执行完之后,就进入到终止状态了。
 所有的线程并不是一调用start()方法就立刻启动,而是要等待CPU调度才可以执行。
3.4、线程的主要操作方法
 线程中的所有操作方法都是在Thread类中定义的。
3.4.1、取得当前正在运行的线程名称
 在Thread类中有以下一个方法可以取得当前正在运行的线程对象。
  • public static Thread currentThread()
 Thread类的构造方法:
  • public Thread(Runnable target,String name)
• public Thread(String name)
 取得线程的名称:public final String getName()
 设置线程的名称:public final void setName(String name)
 可以通过Thread类中的setName()方法设置一个线程的名称,但是最好在线程启动之前进行设置。
例如:以下代码取得了当前正在运行的线程名称
class MyThread implements Runnable{
 public void run(){
  for(int i=0;i<10;i++){
    System.out.println(Thread.currentThread().getName() + "运行, i = " + i) ;
  }
 }
};
public class ThreadDemo06{
 public static void main(String args[]){
  MyThread mt = new MyThread() ;
  new Thread(mt).start() ;
  new Thread(mt).start() ;
  new Thread(mt).start() ;
 }
};
 发现此时线程的名称很有规律:Thread-0、Thread-1、Thread-2,…
 里面肯定有一个static的属性,用于保存当前产生了多少个对象。
例如:直接为线程命名
class MyThread implements Runnable{
 public void run(){
  for(int i=0;i<10;i++){
    System.out.println(Thread.currentThread().getName() + "运行, i = " + i) ;
  }
 }
};
public class ThreadDemo07{
 public static void main(String args[]){
  MyThread mt = new MyThread() ;
  new Thread(mt,"线程A").start() ;
  new Thread(mt,"线程B").start() ;
  new Thread(mt,"线程C").start() ;
  new Thread(mt).start() ;
  new Thread(mt).start() ;
 }
};
分析以下程序的执行结果:
class MyThread implements Runnable{
 public void run(){
  for(int i=0;i<10;i++){
    System.out.println(Thread.currentThread().getName() + "运行, i = " + i) ;
  }
 }
};
public class ThreadDemo08{
 public static void main(String args[]){
  MyThread mt = new MyThread() ;
  new Thread(mt,"线程").start() ;
  mt.run() ;  ? 主方法直接调用的run()方法
 }
};
 观察输出结果:
  • 发现有一个main线程的存在。
 一个JAVA程序至少启动几个线程?是两个
  • MAIN:主线程
  • 垃圾收集线程
3.4.2、线程的休眠
 对于线程可以允许其稍微暂停运行,使用休眠的语法即可完成,方法定义如下:
  • public static void sleep(long millis) throws InterruptedException
  • 此方法会抛出中断异常
例如:以下程序验证了sleep()方法的使用
class MyThread implements Runnable{
 public void run(){
  for(int i=0;i<10;i++){
   try{
    Thread.sleep(500) ;
   }catch(Exception e){}
   System.out.println(Thread.currentThread().getName() + "运行, i = " + i) ;
  }
 }
};
public class ThreadDemo09{
 public static void main(String args[]){
  MyThread mt = new MyThread() ;
  new Thread(mt,"线程").start() ;
  mt.run() ;
 }
};
3.4.3、线程的中断
 之前休眠方法上有一个中断的异常抛出,实际上线程是可以中断的。
 如果要想中断一个线程则必须使用另外一个线程控制,中断线程的方法:
  • public void interrupt()
例如:以下代码演示了中断线程的操作
class MyThread implements Runnable{
 public void run(){
  System.out.println("********* 进入run方法 **********") ;
  try{
   System.out.println(" =========== 开始休眠 ===============") ;
   Thread.sleep(20000) ; 
   System.out.println(" =========== 结束休眠 ===============") ;
  }catch(Exception e){
   System.out.println(e) ;
   // 表示直接返回到方法的调用处
   return ;
  }
  System.out.println("********* 完成run方法 **********") ;
 }
};
public class ThreadDemo10{
 public static void main(String args[]){
  MyThread mt = new MyThread() ;
  Thread t = new Thread(mt,"线程") ;
  t.start() ;
  try{
   // 为了可以让程序多休眠一会儿
   Thread.sleep(1000) ; 
  }catch(Exception e){
  }
  // 中断线程执行
  t.interrupt() ;
 }
};
3.4.4、线程的强制执行
 可以使用一个方法让一个线程强制的执行下去。方法的定义为:
  • public final void join() throws InterruptedException
例如:以下代码演示了join的作用
class MyThread implements Runnable{
 public void run(){
  int i = 0 ;
  while(true){
   System.out.println(Thread.currentThread().getName() + " --> i = " + i++) ;
  }
 }
};
public class ThreadDemo11{
 public static void main(String args[]){
  Thread t = new Thread(new MyThread(),"==线程==") ;
  int i = 0 ;
  t.start() ;
  while(true){
   if(i>=100){
    // 强制运行
    try{
     t.join() ;
    }catch(Exception e){}
   }
   System.out.println("MAIN " + i++) ;
  }
 }
};
3.5、线程的同步与死锁

4、总结
1、 线程可以不掌握代码,但是所有的概念必须非常清楚
2、 进程与线程的区别
 • 线程是在进程的基础之上的进一步划分
 • 如果没有进程了,则线程也不存在
3、 JAVA中线程实现的两种方式
 • 继承Thread类,不能共享数据
 • 实现Runnable接口,可以共享数据
 • 使用Runnable的最大好处是可以避免单继承带来的局限
4、 JAVA中一个java命令实际上启动的是一个JVM进程,一个JVM进程至少启动两个线程:
 • MAIN线程
 • GC线程
5、 线程的主要操作方法
 • currentThread()、getName()、sleep()