Java并发操作——基本的线程机制


1)定义任务

          线程可以驱动任务,因此需要一种描述任务的方式,这可以由Runnable接口来提供(有的地方比如Swing2中经常将其叫做可运行对象)。要想定义任务,只需要实现Runnable接口并编写run()方法,使得该任务执行命令。 但是此时呢,这个run()方法跟一个普通方法一样,并没有什么特殊的地方,不会产生任何内在的线程能力。要实现线程行为,我们必须在使用的时候显示的将一个任务附着到现场上。

2)Thread类

          要想Runnable对象要想具有线程行为,传统的方式就是把它提交给一个Thread构造器。然后调用Thread类的start()方法为该线程执行必须的初始化操作,继而调用Runnable的run()方法。

      下面的代码简单演示Thread的用法

 

public class LiftOff implements Runnable {
   
    public int countDown =10;
   
    private static int taskCount = 0;
   
    private final int id = taskCount++;
   
    public LiftOff() {
        System.out.println("默认构造函数");
    }

    public LiftOff(int countDown) {
        System.out.println("非默认的构造函数");
        this.countDown = countDown;
    }
   
    public String getStatus() {
        return "#" + id + "(" + (countDown > 0 ? countDown : "LiftOff") + ").";
    }
   
    @Override
    public void run() {
        while(countDown-->0) {
            System.out.print(getStatus());
            Thread.yield();
        }
    }

}

 

 

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ThreadTestWithExecutor {

    private static final int count = 5;
   
    /**
    * @param args
    */
    public static void main(String[] args) {
        //1.什么都不加。
        for(int i=0;i<count;i++) {
          Thread t = new Thread(new LiftOff());
          t.start();
      }
        System.out.println("执行完毕!");
    }

}

 

第一次执行结果

 

默认构造函数
默认构造函数
默认构造函数
默认构造函数
默认构造函数
执行完毕!
#0(9).#0(8).#0(7).#1(9).#3(9).#2(9).#4(9).#0(6).#2(8).#4(8).#0(5).#2(7).#4(7).#0(4).#2(6).#4(6).#3(8).#2(5).#4(5).#2(4).#2(3).#4(4).#1(8).#3(7).#2(2).#1(7).#3(6).#2(1).#1(6).#3(5).#0(3).#2(LiftOff).#1(5).#3(4).#4(3).#1(4).#3(3).#0(2).#4(2).#1(3).#3(2).#0(1).#3(1).#1(2).#4(1).#1(1).#3(LiftOff).#0(LiftOff).#1(LiftOff).#4(LiftOff)

 

第二次执行结果

 

默认构造函数
默认构造函数
默认构造函数
默认构造函数
默认构造函数
执行完毕!
#1(9).#3(9).#1(8).#3(8).#1(7).#3(7).#1(6).#3(6).#1(5).#3(5).#1(4).#3(4).#1(3).#3(3).#1(2).#3(2).#1(1).#3(1).#1(LiftOff).#3(LiftOff).#0(9).#2(9).#4(9).#0(8).#2(8).#4(8).#0(7).#2(7).#4(7).#0(6).#2(6).#4(6).#0(5).#2(5).#4(5).#0(4).#2(4).#4(4).#0(3).#2(3).#4(3).#0(2).#2(2).#4(2).#0(1).#2(1).#4(1).#0(LiftOff).#2(LiftOff).#4(LiftOff).

 

从上述两次的执行结果中,我们可以得到以下结论:

    (1)多线程的执行不是按照顺序执行的,各个线程之间相互交叉。
        #0(9).#0(8).#0(7).#1(9).#3(9).#2(9) 说明可Runnable0还没执行完,Runnable2 就执行了。

    (2)start()方法看起来是产生了一个对长期运行方法的调用(即run方法),但是从实际的输出情况来看,start()方法迅速的返回了,因为“执行完毕”在run()方法执行完毕之前就已经输出了。实际上,我们产生的是对Runnable.run()方法的调用,并且这个方法还没有完成,但是因为这个方法是由不同的线程执行的,因此我们仍然可以执行main()线程中的其他操作(当然这种能力并不局限于main()线程,任何线程都可以启动另一个线程)。因此这时候程序会同时运行两个方法、

    (3)比较两次执行结果可以发现,两次执行的结果不一样。这是因为线程调度机制是非确定的。

 3)使用Exexutor

        在Java SE5的java.util.concurrent包中的执行器(Executor)将为我们管理Thread对象,从而简化了并发编程。

    下面的代码就列举了几种不同Executor的执行情况:

 

public class LiftOff implements Runnable {
   
    public int countDown =10;
   
    private static int taskCount = 0;
   
    private final int id = taskCount++;
   
    public LiftOff() {
        System.out.println("默认构造函数");
    }

    public LiftOff(int countDown) {
        System.out.println("非默认的构造函数");
        this.countDown = countDown;
    }
   
    public String getStatus() {
        return "#" + Thread.currentThread().getId() + "(" + (countDown > 0 ? countDown : "LiftOff") + ").";
    }
   
    @Override
    public void run() {
        while(countDown-->0) {
            System.out.print(getStatus());
            Thread.yield();
        }
    }

}

 

 

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* CachedThreadPool
*/
public class ThreadTestWithExecutor {

   
    private static final int count = 5;
   
    /**
    * @param args
    */
    public static void main(String[] args) {
       
        ExecutorService exec = Executors.newCachedThreadPool();
        for(int i=0;i<count;i++) {
            exec.execute(new LiftOff());
        }
        exec.shutdown();


          //3.使用FixThreadPool
            ExecutorService exec2 = Executors.newFixedThreadPool(3);
            for(int i=0;i<count;i++) {
                exec2.execute(new LiftOff());
              }
          exec2.shutdown();

          //4.SingleThreadExecutor
          ExecutorService exec3 = Executors.newSingleThreadExecutor();
          for(int i=0;i<count;i++) {
              exec3.execute(new LiftOff());
              }
            exec.shutdown();
        System.out.println("执行完毕!"); 

    }

}

 

这几段代码,都很简单,但是有几点需要说明

    1)shutdown()方法的调用可以防止新任务被提交给这个Executor,当前线程(在上述代码中就是驱动main()的线程)将继续运行在shutdown()被调用之前提交的所有任务。

换句说,虽然是shutdown了,但是当前在执行器中的线程仍然会继续执行。

    2)FixedThreadPool可以一次性预先执行代价高昂的线程分配(因而就可以限制线程的数量了)。这样会节省时间,因为你不用为每个任务都固定的付出创建线程的时间(记住,虽然每次new了一个Runnable对象,但是并不是每个Runnable对象都会new一次Thread.),直接从线程池中取线程即可。 但是,我们一定要记住一点,在任何线程池中,现有线程在可能的情况下, 都会被复用。

  3)SingleThreadExecutor就像是线程数量为1的FixedThreadPool。这时候如果向其提交了多个任务,这些任务就会排队。每个任务都会在下一个任务开始之前运行结束。所有的任务将使用相同的线程。

大话设计模式(带目录完整版) PDF+源代码

Java中介者设计模式

Java 设计模式之模板方法开发中应用

设计模式之 Java 中的单例模式(Singleton)

本文永久更新链接地址:

相关内容