Java 线程总结

今天准备总结一下关于Java 线程的问题,提到线程很容易与进程混淆,从计算机操作系统的发展来看,经历了这样的两个阶段:

       单进程处理:最早以前的DOS 系统就属于单进程处理,即:在同一个时间段上只能有一个程序在执行,所以在DOS 系统中只要有病毒的出现,则立刻会有反映;

       多进程处理:我们现在使用的Windows 操作系统就是典型的一个多线程,所以,如果在windows 中出现病毒了,则系统照样可以使用,通过Ctrl+Shift+delete 可以查看windows 系统的具体进程情况;

       那么对于资源来讲,所有的IO 设备、CPU 等等只有一个,那么对于多线程的处理来讲,在同一个时间段 上会有多个程序运行,但是在同一个时间点 上只能有一个程序运行。所以我们可以发现线程是在进程的基础上进一步的划分,我们可以举个这样的例子,Eclipse 中对Java 的关键字的检查,是在Eclipse 整个程序运行中检测运行的。因此进程中止了,线程也随之中止。但是线程中止了,进程可能依然会执行。我们可以这样理解,进程是一个静态的概念,一个任务或者说一个程序,一个进程里有一个主线程。

       下面我们来看看Java 中对线程处理机制的支持,在Java 语言中对线程的实现有两种方恨死:一个是继承Thread类,另一个是实现Runnable 接口。下面我们来分别来看看这两种实现方式:      (剑道独尊

继承Thread 类:

       一个java 类只要继承了Thread 类   ,同时覆写了本类中的run() 方法,则就可以实现Java 中的多线程操作了。

MyThread.java :

Java代码   收藏代码
  1. package com.iflytek.thread;  
  2.   
  3. /** 
  4.  * @author xudongwang 2012-1-1 
  5.  *  
  6.  *         Email:xdwangiflytek@gmail.com 
  7.  */  
  8. public class MyThread extends Thread {  
  9.   
  10.     private String name;  
  11.   
  12.     public MyThread(String name) {  
  13.         this.name = name;  
  14.     }  
  15.   
  16.     public void run() {// 覆写run()方法  
  17.         for (int i = 0; i < 10; i++) {  
  18.             System.out.println("Thread运行:" + name + ",i=" + i);  
  19.         }  
  20.     }  
  21.   
  22. }  

 

下面我们来实现上面的多线程操作类,MyThreadTest.java :

Java代码   收藏代码
  1. package com.iflytek.thread;  
  2.   
  3. /** 
  4.  * @author xudongwang 2012-1-1 
  5.  *  
  6.  *         Email:xdwangiflytek@gmail.com 
  7.  */  
  8. public class MyThreadTest {  
  9.   
  10.     public static void main(String[] args) {  
  11.         MyThread thread1 = new MyThread("线程A");  
  12.         MyThread thread2 = new MyThread("线程B");  
  13.   
  14.         thread1.run();// 调用线程  
  15.         thread2.run();  
  16.     }  
  17.       
  18. }  
 

通过运行结果,我们可以发现其执行的结果非常有规律,先执行完第一个对象,再执行完第二个对象的,即没有实现交互的现象;

通过JDK 文档可以发现,一旦我们调用Start() 方法,则会通过JVM 找到run() 方法。所以当我们将上面调用的run() 方法改为start() 方法:

Java代码   收藏代码
  1. package com.iflytek.thread;  
  2.   
  3. /** 
  4.  * @author xudongwang 2012-1-1 
  5.  *  
  6.  *         Email:xdwangiflytek@gmail.com 
  7.  */  
  8. public class MyThreadTest {  
  9.   
  10.     public static void main(String[] args) {  
  11.         MyThread thread1 = new MyThread("线程A");  
  12.         MyThread thread2 = new MyThread("线程B");  
  13.   
  14.         thread1.start();// 调用线程  
  15.         thread2.start();  
  16.     }  
  17.       
  18. }  

 

这时再去执行发现结果有交互的现象,所以这也值得我们思考为什么非要使用start() 方法启动多线程呢?通过查看Java 源码:

Java代码   收藏代码
  1. public synchronized void start() {//定义start方法  
  2.             /** 
  3.          * This method is not invoked for the main method thread or "system" 
  4.          * group threads created/set up by the VM. Any new functionality added  
  5.          * to this method in the future may have to also be added to the VM. 
  6.          * 
  7.          * A zero status value corresponds to state "NEW". 
  8.              */  
  9.             if (threadStatus != 0 || this != me)//判断线程是否已经启动  
  10.                 throw new IllegalThreadStateException();  
  11.             group.add(this);  
  12.             start0();//调用start0方法  
  13.             if (stopBeforeStart) {  
  14.             stop0(throwableFromStop);  
  15.         }  
  16.         }  
  17.   
  18.         private native void start0();//使用native关键字声明的方法没有方法体  

 

说明:操作系统有很多种,Windows 、Linux 、UNIX ,既然多线程操作中要进行CPU 资源的强占,也就是说要等待CPU 调度,那么这些调度的操作是由各个操作系统的底层实现的,所以在Java 程序中根本就没法实现,那么此时Java 的设计者定义了native 关键字,使用此关键字表示可以调用操作系统的底层函数,那么这样的技术又称为JNI技术(Java Native Interface ),而且,此方法在执行的时候将调用run 方法完成,由系统默认调用的。

下面我们看看线程的状态:

 

实现Runnable 接口:

       因为我们知道继承的单一继承的局限性,所以我们在开发中一个多线程的操作类很少去使用Thread 类完成,而是通过Runnable 接口完成。

 

       查看源码发现Runnable 的定义:

Java代码   收藏代码
  1. public interface Runnable {  
  2.     public abstract void run();  
  3. }  

 

所以一个类只要实现了此接口,并覆写run() 方法

 

Java代码   收藏代码
  1. package com.iflytek.thread;  
  2.   
  3. /** 
  4.  *  
  5.  * @author xudongwang 2012-1-1 
  6.  *  
  7.  *         Email:xdwangiflytek@gmail.com 
  8.  */  
  9. public class MyThreadByRunnable implements Runnable {  
  10.   
  11.     private String name;  
  12.   
  13.     public MyThreadByRunnable(String name) {  
  14.         this.name = name;  
  15.     }  
  16.   
  17.     public void run() {// 覆写run()方法  
  18.         for (int i = 0; i < 10; i++) {  
  19.             System.out.println("Thread运行:" + name + ",i=" + i);  
  20.         }  
  21.     }  
  22. }  

 

有了多线程操作类下面我们需要启动多线程,但是在现在使用Runnable 定义的子类中并没有start() 方法,而只有Thread 类中才有,在Thread 类中存在以下的一个构造方法:

Java代码   收藏代码
  1. public Thread(Runnable target) {  
  2. init(null, target, "Thread-" + nextThreadNum(), 0);  
  3.    }  

 

此构造方法接受Runnable 的子类实例,也就是说现在我们可以通过Thread 类来启动Runnable 实现的多线程。

 

Java代码   收藏代码
  1. package com.iflytek.thread;  
  2.   
  3. /** 
  4.  *  
  5.  * @author xudongwang 2012-1-1 
  6.  *  
  7.  *         Email:xdwangiflytek@gmail.com 
  8.  */  
  9. public class MyThreadByRunnableTest {  
  10.     public static void main(String[] args) {  
  11.   
  12.         MyThreadByRunnable thread1 = new MyThreadByRunnable("线程A");  
  13.         MyThreadByRunnable thread2 = new MyThreadByRunnable("线程B");  
  14.         new Thread(thread1).start();  
  15.         new Thread(thread2).start();  
  16.   
  17.     }  
  18.   
  19. }  

当然上面的操作代码也属于交替的运行,所以此时程序也同样实现了多线的操作;

 

下面我们来总结一下两种实现方式的区别及联系:

       在程序的开发中只要是多线程则肯定永远以实现Runnable 接口为正统操作,因为实现Runnable 接口相比继承Thread 类有如下的好处:

1、 避免单继承的局限性,一个类可以同时实现多个接口

2、 适合于资源的共享

 

下面来说说关于线程的几个Demo ;

1 、两个线程访问同一个对象,ThreadSyncDemo.java :

Java代码   收藏代码
  1. package com.iflytek.thread;  
  2.   
  3. /** 
  4.  * @author xudongwang 2012-1-1 
  5.  *  
  6.  *         Email:xdwangiflytek@gmail.com 
  7.  */  
  8. public class ThreadSyncDemo implements Runnable {  
  9.     Timer timer = new Timer();  
  10.   
  11.     public static void main(String[] args) {  
  12.         ThreadSyncDemo threadSyncDemo = new ThreadSyncDemo();  
  13.         Thread thread1 = new Thread(threadSyncDemo);  
  14.         Thread thread2 = new Thread(threadSyncDemo);  
  15.         thread1.setName("t1");// 修改线程名称  
  16.         thread2.setName("t2");  
  17.         thread1.start();  
  18.         thread2.start();  
  19.     }  
  20.   
  21.     @Override  
  22.     public void run() {  
  23.         timer.add(Thread.currentThread().getName());  
  24.     }  
  25. }  
  26.   
  27. class Timer {  
  28.     private static int num = 0;  
  29.   
  30.     public void add(String name) {  
  31.         num++;  
  32.         try {  
  33.             // 第一个线程执行到 这里时被休眠了,这是num为1,而第二个线程重新来执行时num为2,并休眠,而此时第一个线程启动了  
  34.             Thread.sleep(1);  
  35.         } catch (InterruptedException e) {  
  36.         }  
  37.         System.out.println(name + ",你是第" + num + "个使用timer的线程");  
  38.     }  
  39. }  

 

运行结果:

t1, 你是第 2 个使用 timer 的线程

t2, 你是第 2 个使用 timer 的线程

而如果程序这样改动一下,ThreadSyncDemo02.java :

Java代码   收藏代码
  1. package com.iflytek.thread;  
  2.   
  3. /** 
  4.  * @author xudongwang 2012-1-1 
  5.  *  
  6.  *         Email:xdwangiflytek@gmail.com 
  7.  */  
  8. public class ThreadSyncDemo02 implements Runnable {  
  9.     Timer02 timer = new Timer02();  
  10.   
  11.     public static void main(String[] args) {  
  12.         ThreadSyncDemo02 threadSyncDemo = new ThreadSyncDemo02();  
  13.         Thread thread1 = new Thread(threadSyncDemo);  
  14.         Thread thread2 = new Thread(threadSyncDemo);  
  15.         thread1.setName("t1");// 修改线程名称  
  16.         thread2.setName("t2");  
  17.         thread1.start();  
  18.         thread2.start();  
  19.     }  
  20.   
  21.     @Override  
  22.     public void run() {  
  23.         timer.add(Thread.currentThread().getName());  
  24.     }  
  25. }  
  26.   
  27. class Timer02 {  
  28.     private static int num = 0;  
  29.   
  30.     public synchronized void add(String name) {// 执行这个方法的过程之中,当前对象被锁定  
  31.         synchronized (this) {// 这样的话,在{}中的线程执行的过程中不会被另一个线程打断,也就是说{}只能有一个线程  
  32.             num++;  
  33.             try {  
  34.                 Thread.sleep(1);// 第一个线程执行到  
  35.                                 // 这里时被休眠了,这是num为1,而第二个线程重新来执行时num为2,并休眠,而此时第一个线程启动了  
  36.             } catch (InterruptedException e) {  
  37.             }  
  38.             System.out.println(name + ",你是第" + num + "个使用timer的线程");  
  39.         }  
  40.     }  
  41. }  

  运行结果:

t1, 你是第 1 个使用 timer 的线程

t2, 你是第 2 个使用 timer 的线程

 

2 、死锁,ThreadDieDemo.java :

Java代码   收藏代码
  1. package com.iflytek.thread;  
  2.   
  3. /** 
  4.  * @author xudongwang 2012-1-1 
  5.  *  
  6.  *         Email:xdwangiflytek@gmail.com 
  7.  */  
  8. public class ThreadDieDemo {  
  9.   
  10.     public static void main(String[] args) {  
  11.         DeadLock lock1 = new DeadLock();  
  12.         DeadLock lock2 = new DeadLock();  
  13.         lock1.flag = 1;  
  14.         lock2.flag = 2;  
  15.         Thread thread1 = new Thread(lock1);  
  16.         Thread thread2 = new Thread(lock2);  
  17.         thread1.start();  
  18.         thread2.start();  
  19.     }  
  20. }  
  21.   
  22. class DeadLock implements Runnable {  
  23.     public int flag = 1;  
  24.     static Object o1 = new Object();  
  25.     static Object o2 = new Object();  
  26.   
  27.     @Override  
  28.     public void run() {  
  29.         System.out.println("flag = " + flag);  
  30.         if (flag == 1) {  
  31.             synchronized (o1) {  
  32.                 try {  
  33.                     Thread.sleep(500);  
  34.                 } catch (InterruptedException e) {  
  35.                     e.printStackTrace();  
  36.                 }  
  37.                 synchronized (o2) {  
  38.                     System.out.println("o2");  
  39.                 }  
  40.             }  
  41.         }  
  42.         if (flag == 2) {  
  43.             synchronized (o2) {  
  44.                 try {  
  45.                     Thread.sleep(500);  
  46.                 } catch (InterruptedException e) {  
  47.                     e.printStackTrace();  
  48.                 }  
  49.                 synchronized (o1) {  
  50.                     System.out.println("o1");  
  51.                 }  
  52.             }  
  53.         }  
  54.     }  
  55. }  

 

 

3 、生产者和消费者问题:

首先简单说明一下sleep 、wait 、notify 的区别:

       sleep :sleep 是在Thread 中的,同时在sleep 的时候锁还在;

        wait :wait 必须是在锁住对象时才能wait ,同时在wait 的时候,锁就不在归那个对象所有了,而在其方法定义在Object 中,它是让进入到此锁住对象的线程wait ;

notify :与wait 相对应,叫醒一个现在正在wait 在我这个对象上的线程,谁现在正在我这个对象上等待,我就叫醒这个线程让他继续执行,他也是Object 类中的方法;(召唤美女军团)

ProductCustomerDemo.java :

Java代码   收藏代码
  1. package com.iflytek.thread;  
  2.   
  3. /** 
  4.  * @author xudongwang 2012-1-1 
  5.  *  
  6.  *         Email:xdwangiflytek@gmail.com 
  7.  */  
  8. public class ProductCustomerDemo {  
  9.     public static void main(String[] args) {  
  10.         WoToStack woToStack = new WoToStack();  
  11.         Product product = new Product(woToStack);  
  12.         Customer customer = new Customer(woToStack);  
  13.         new Thread(product).start();  
  14.         new Thread(customer).start();  
  15.     }  
  16. }  
  17.   
  18. /** 
  19.  * 消费和生产的对象 
  20.  *  
  21.  * @author xudongwang 2012-1-1 
  22.  *  
  23.  *         Email:xdwangiflytek@gmail.com 
  24.  */  
  25. class WoTo {  
  26.     int id;  
  27.   
  28.     public WoTo(int id) {  
  29.         this.id = id;  
  30.     }  
  31.   
  32.     @Override  
  33.     public String toString() {  
  34.         return "WoTo [id=" + id + "]";  
  35.     }  
  36. }  
  37.   
  38. class WoToStack {  
  39.     int index = 0;  
  40.     WoTo[] arrayWoTo = new WoTo[10];// 这里限制一下,框子最多装10个WoTo  
  41.   
  42.     /** 
  43.      * 向框子中放WoTo 
  44.      *  
  45.      * @param wt 
  46.      */  
  47.     public synchronized void push(WoTo wt) {  
  48.         // 这里用while是因为如果被打断还要执行判断,而如果是if则会直接进入下一个语句  
  49.         while (index == arrayWoTo.length) {  
  50.             try {  
  51.                 this.wait();  
  52.             } catch (InterruptedException e) {  
  53.                 e.printStackTrace();  
  54.             }  
  55.         }  
  56.         this.notifyAll();  
  57.         arrayWoTo[index] = wt;  
  58.         index++;  
  59.     }  
  60.   
  61.     /** 
  62.      * 从框子中去WoTo 
  63.      *  
  64.      * @return 
  65.      */  
  66.     public synchronized WoTo pop() {  
  67.         while (index == 0) {  
  68.             try {  
  69.                 this.wait();  
  70.             } catch (InterruptedException e) {  
  71.                 e.printStackTrace();  
  72.             }  
  73.         }  
  74.         this.notifyAll();  
  75.         index--;  
  76.         return arrayWoTo[index];  
  77.     }  
  78. }  
  79.   
  80. /** 
  81.  * 生产者 
  82.  *  
  83.  * @author xudongwang 2012-1-1 
  84.  *  
  85.  *         Email:xdwangiflytek@gmail.com 
  86.  */  
  87. class Product implements Runnable {  
  88.     // 首先生产者需要知道生产WoTo放在哪里  
  89.     WoToStack stack = null;  
  90.   
  91.     public Product(WoToStack stack) {  
  92.         this.stack = stack;  
  93.     }  
  94.   
  95.     @Override  
  96.     public void run() {  
  97.         for (int i = 0; i < 20; i++) {// 这里我们限制一下每一个生产者可以生产20个WoTo  
  98.             WoTo woTo = new WoTo(i);  
  99.             stack.push(woTo);  
  100.             System.out.println("生产者生产了 :" + woTo);  
  101.             try {  
  102.                 Thread.sleep((int) Math.random() * 200);  
  103.             } catch (InterruptedException e) {  
  104.                 e.printStackTrace();  
  105.             }  
  106.         }  
  107.     }  
  108. }  
  109.   
  110. class Customer implements Runnable {  
  111.     WoToStack stack = null;  
  112.   
  113.     public Customer(WoToStack stack) {  
  114.         this.stack = stack;  
  115.     }  
  116.   
  117.     @Override  
  118.     public void run() {  
  119.         for (int i = 0; i < 20; i++) {// 这里我们也限制一下每一个小费者可以消费20个WoTo  
  120.             WoTo woTo = stack.pop();  
  121.             System.out.println("消费者消费了 :" + woTo);  
  122.             try {  
  123.                 Thread.sleep((int) Math.random() * 1000);  
  124.             } catch (InterruptedException e) {  
  125.                 e.printStackTrace();  
  126.             }  
  127.         }  
  128.     }  
  129. }  

 

 

4 、卖票问题(Runnable 资源共享):

MyThread.java:

Java代码   收藏代码
  1. package com.iflytek.maipiao;  
  2.   
  3. /** 
  4.  * @author xudongwang 2012-1-1 
  5.  *  
  6.  *         Email:xdwangiflytek@gmail.com 
  7.  */  
  8. public class MyThread extends Thread {  
  9.   
  10.     private int ticket = 5;// 一共5张票  
  11.   
  12.     public void run() {  
  13.         for (int i = 0; i < 50; i++) {  
  14.             if (this.ticket > 0) {  
  15.                 System.out.println("卖票:ticket = " + this.ticket--);  
  16.             }  
  17.         }  
  18.     }  
  19.   
  20. }  

 

下面建三个线程对象,同时卖票,ThreadTicket.java :

Java代码   收藏代码
  1. package com.iflytek.maipiao;  
  2.   
  3. /** 
  4.  * @author xudongwang 2012-1-1 
  5.  *  
  6.  *         Email:xdwangiflytek@gmail.com 
  7.  */  
  8. public class ThreadTicket {  
  9.   
  10.     public static void main(String[] args) {  
  11.         MyThread thread1 = new MyThread();  
  12.         MyThread thread2 = new MyThread();  
  13.         MyThread thread3 = new MyThread();  
  14.   
  15.         // 开始卖票  
  16.         thread1.start();  
  17.         thread2.start();  
  18.         thread3.start();  
  19.     }  
  20.   
  21. }  

运行发现一共卖了15 张票,但是实际上只有5 张票,所以证明每一个线程都卖自己的票,这样就没有达到资源共享的目的。

其实我们使用Runnable 接口的话,则就可以实现资源的共享:

MyThreadByRunnable.java :

Java代码   收藏代码
  1. package com.iflytek.maipiao;  
  2.   
  3. /** 
  4.  * @author xudongwang 2012-1-1 
  5.  *  
  6.  *         Email:xdwangiflytek@gmail.com 
  7.  */  
  8. public class MyThreadByRunnable implements Runnable {  
  9.   
  10.     private int ticket = 5;// 一共5张票  
  11.   
  12.     public void run() {  
  13.         for (int i = 0; i < 50; i++) {  
  14.             if (this.ticket > 0) {  
  15.                 System.out.println("卖票:ticket = " + this.ticket--);  
  16.             }  
  17.         }  
  18.     }  
  19.   
  20. }  

同样,我们再弄一个多线程进行卖票的操作,RunnableTicket.Java :

Java代码   收藏代码
  1. package com.iflytek.maipiao;  
  2.   
  3. /** 
  4.  * @author xudongwang 2012-1-1 
  5.  *  
  6.  *         Email:xdwangiflytek@gmail.com 
  7.  */  
  8. public class RunnableTicket {  
  9.   
  10.     public static void main(String[] args) {  
  11.         MyThreadByRunnable threadByRunnable = new MyThreadByRunnable();  
  12.         new Thread(threadByRunnable).start();  
  13.         new Thread(threadByRunnable).start();  
  14.         new Thread(threadByRunnable).start();  
  15.     }  
  16. }  
 

虽然现在程序中有三个线程,但是从运行结果上看,三个线程一共卖出了5 张票,也就是说使用Runnable 实现的多线程可以达到资源共享的目的。

实际上,Runnable 接口和Thread 类之间还是存在联系的

Java代码   收藏代码
  1. Public class Thread implements Runnable {  

 

发现Thread 类也是Runnable 接口的子类。

在实际的开发中比如说发多个邮件提醒等都会用到线程的,所以线程还是很重要的;


转自:http://xdwangiflytek.iteye.com/blog/1333128

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值