疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

多线程的使用


 

1、要讲java多线程,首先要说什么是线程,我们先来说说进程的概念,进程是系统资源分派和调度的基本单位,需要在内存为进程分配空间。像运行exe文件就会在系统生成一个对应的进程。线程存在于进程,是进程中实际执行代码的部分,一个进程可能只有一个主线程也可能有多个线程,线程是cpu分派和调度的基本单位。像运行java程序,生成一个进程,而实际执行main代码的部分就是主线程部分。
2、java中如何使用多线程编程,java为多线程封装了一个类Thread,生成线程有两种方式。
1)继承Thread类

public class MyThread extends Thread{
  public MyThread(String threadName){
    super(threadName);
  }
  public void run(){
    System.out.println(Thread.currentThread()+":run");
  }
  public static void main(String[] args){
    MyThread myThread=new MyThread();
    myThread.start();
  }
}
/code]
2)实现Runnable接口
Java代码
  1. public class MyRunnable implements Runnable{   
  2.   public void run(){   
  3.     System.out.println("I run");   
  4.   }   
  5.   public static void main(String[] args){   
  6.      MyRunnable myRunnable=new MyRunnable();   
  7.      Thread thread=new Thread(myRunnable);   
  8.      thread.start();   
  9.   }      
  10. }  

两种实现最后都是调用Thread类或其派生类的start方法开启线程,如果是继承Thread的类,在调用start方法后,在开启的线程中会调用Thread类的run()方法,所以如果继承Thread类的话,只要在派生类中重写run()方法并在方法中加入需要在线程中执行的代码。如果直接调用run()方法的话就是普通的方法调用,并没有生成线程。如果是实现Runnable接口的话,从代码中可以看出实现类的实例会作为参数传给Thread对象,然后调用Thread对象的start方法,所以这种方式下开启线程还是执行的是Thread类的run()方法,然后再run()方法中调用Runnable实现类的run()方法,这里有点像代理模式,Thread类是Runnable实现类的代理。
Runnable的实现类的同一个对象可以传给多个Thread对象。
Java代码
  1. MyRunnable myRunnable=new MyRunnable();   
  2. Thread thread1=new Thread(myRunnable);   
  3. Thread thread2=new Thread(myRunnable);   
  4. ....   
  5. thread1.start();   
  6. thread2.start();   
  7. .....  

但是在实际应用中,一个MyRunnable对象一般只是给一个线程使用,所以我们在实际开发中这样的代码更加常见
Java代码
  1. new Thread(new Runnable{   
  2.   public void run(){   
  3.     .....   
  4.   }}.start();  

什么时候用实现Runnable接口,什么时候继承Thread类呢
1.由于java的单继承,如果继承了Thread类,就不能继承其他类,所以需要继承其他类的时候需要实现Runnable接口。
2.一般来说能用继承Thread的地方都能用实现Runnable接口,所以有时为了减少生成的类,比如后面使用实现Runnable的方式,所以个人认为尽量用Runnable,而且如果用线程池,Runnable实现类的对象可以直接被线程池的接口类ExecutorService执行。 
  java1.5引进了java.util.concurrent包,引入了很多用于并发处理的类,这里主要讲讲线程池相关的类和接口。
ExecutorService接口,继承自Executor接口,
这里主要讲我们平时在项目中经常用到的几个方法
void execute(Runnable command);执行Runnable任务
<T> Future<T> submit(Callable<T> task);执行Callable任务,与Runnable任务的最大区别是它有返回值
Future<?> submit(Runnable task);执行Runnable任务,与execute没有多大区别,虽然有返回值,但是返回对象Future中取得的值为null
void shutdown();如果执行了,线程池不再接受新任务,但是会执行完已经提交的任务。
ExecutorService的实现类ThreadPoolExecutor,
它有个构造函数
Java代码
  1. public ThreadPoolExecutor(int corePoolSize,//核心线程数   
  2.                             int maximumPoolSize,//最大线程数   
  3.                             long keepAliveTime,//线程活动时间   
  4.                             TimeUnit unit,//时间单位   
  5.                       BlockingQueue<Runnable> workQueue//任务接收队列  

通过传入不同的参数可以得到不同的线程池对象。
java.util.concurrent包下有个帮助类Executors类,它提供了返回ThreadPoolExecutor对象的方法。
Java代码
  1. public static ExecutorService newCachedThreadPool() {   
  2.     return new ThreadPoolExecutor(0, Integer.MAX_VALUE,   
  3.                                   60L, TimeUnit.SECONDS,   
  4.                                   new SynchronousQueue<Runnable>());   
  5. }  

从调用的构造函数可以看出,当调用newCachedThreadPool()得到线程池处理对象时,核心线程数是0,最大线程数不限,任务接收队列每次只能接收一个任务,如果当前运行的线程数<核心线程数,则当任务到来的时候生成一个新的线程用于执行任务,这里核心线程数是0,所以有任务到来的时候首先将其放入等待队列,然后从队列中取走线程,如果有空闲线程,则用空闲线程执行任务,如果没有空闲线程,则生成新的线程执行任务,SynchronousQueue这个队列,对任务这么处理,如果队列是空的,则将任务加入队列,否则任务阻塞,只有队列中的任务被取走后,才加入队列。(这里个人是这么理解的,也不是特别了解,如果我讲错了,希望读者指出,我会改正)
Java代码
  1. public static ExecutorService newFixedThreadPool(int nThreads) {   
  2.      return new ThreadPoolExecutor(nThreads, nThreads,   
  3.                                    0L, TimeUnit.MILLISECONDS,   
  4.                                    new LinkedBlockingQueue<Runnable>());   
  5.  }  


  这里核心线程数等于最大线程数,也就是传入的线程数,线程池将维护指定数目的线程。这里时间参数为0,表示无乱线程空闲多少时间都不会将其关闭。当有任务到来的时候,如果有空闲线程,则用空闲线程来执行任务,如果线程都忙着,查看当前线程数是否小于核心线程,如果小于则开启新线程,否则则将其加入等待队列,这里的等待队列是LinkedBlockingQueue结构,这是个链表结构的队列,无论加入多少任务,都不会满
Java代码
  1. public static ExecutorService newSingleThreadExecutor() {   
  2.     return new FinalizableDelegatedExecutorService   
  3.         (new ThreadPoolExecutor(11,   
  4.                                 0L, TimeUnit.MILLISECONDS,   
  5.                                 new LinkedBlockingQueue<Runnable>()));   
  6. }  

这个和fixed的线程池差不多,只是核心线程数变成了1,所以加入的任务,根据加入的顺序用单线程来执行。
下面看下用线程池来执行Runnable任务的代码
Java代码
  1. MyRunnable myRunnable=new MyRunnable();   
  2. ExecutorService executorService=null;   
  3.         executorService=Executors.newCachedThreadPool();   
  4. executorService.execute(myRunnable);   
  5. executorService=Executors.newFixedThreadPool(10);   
  6. executorService.execute(myRunnable);   
  7. executorService=Executors.newSingleThreadExecutor();   
  8. executorService.execute(myRunnable);  

线程池提供了比原始多线程使用更多的功能和更方便的使用,而且也更加高效,所以在实际项目中尽量用线程池来开启新线程执行代码。