java programming

Core Java Volume I 读书笔记—第十四章 多线程

内容概要: java多线程并发 , java核心编程

java多线程并发

进程与线程的本质区别: 每个进行有自己独立的变量空间, 而线程则共享数据

(FYI:回忆下Java的运行时内存布局,  Java 堆是线程共享的)

14.1 什么是线程

Java 标准类库中的线程类  java.lang.Thread,  Thread 类的内容非常多, 这里截取一些常用的, 更多信息参考 java.lang.Thread.java :

class Threadimplements Runnable {

   privatestatic native void registerNatives();
    static{
        registerNatives();
   }

   privatevolatile Stringname;
    private int           priority;
    privateThread         threadQ;
    private long          eetop;

   /*Whether or not to single_step this thread. */
   privateboolean     single_step;

   /*Whether or not the thread is a daemon thread. */
   privateboolean     daemon= false;

   /*JVM state */
   privateboolean     stillborn= false;

   /*What will be run. */
   privateRunnabletarget;

   /*The group of this thread */
   privateThreadGroupgroup;

   /*The context ClassLoader for this thread */
   privateClassLoadercontextClassLoader;

   /*The inherited AccessControlContext of this thread */
   privateAccessControlContextinheritedAccessControlContext;

   /*For autonumbering anonymous threads. */
   privatestatic int threadInitNumber;
    private static synchronized intnextThreadNum() {
       return threadInitNumber++;
   }

   ThreadLocal.ThreadLocalMapthreadLocals= null;


   ThreadLocal.ThreadLocalMapinheritableThreadLocals= null;

   privatelong stackSize;

   privatelong nativeParkEventPointer;
   /*
     * Thread ID
     */
   privatelong tid;

   /*For generating thread ID */
   privatestatic long threadSeqNumber;

   privatevolatile int threadStatus= 0;


    private static synchronized longnextThreadID() {
       return ++threadSeqNumber;
   }


   volatile ObjectparkBlocker;

   privatevolatile Interruptibleblocker;
    private finalObject blockerLock= new Object();


   voidblockedOn(Interruptibleb) ;


   public final static int MIN_PRIORITY=1;
   public final static int NORM_PRIORITY=5;
   public final static int MAX_PRIORITY=10;

   public static native ThreadcurrentThread();
   public static native void yield();

   public static native void sleep(longmillis) throwsInterruptedException;


   public static void sleep(longmillis, intnanos)
   throwsInterruptedException;


   private void init(ThreadGroup g,Runnable target,String name,
                      longstackSize) {
        init(g, target,name, stackSize, null);
   }


   private void init(ThreadGroup g,Runnable target,String name,
                      longstackSize,AccessControlContext acc);
   @Override
   protectedObjectclone() throws CloneNotSupportedException {
       throw new CloneNotSupportedException();
   }


   public Thread() {
        init(null, null, "Thread-"+nextThreadNum(), 0);
   }
   public Thread(Runnable target) {
        init(null, target,"Thread-" +nextThreadNum(),0);
   }
   Thread(Runnable target,AccessControlContextacc) {
        init(null, target,"Thread-" +nextThreadNum(),0,acc);
   }
   public Thread(ThreadGroup group,Runnable target) {
        init(group, target,"Thread-" +nextThreadNum(),0);
   }
   public Thread(String name) {
        init(null, null, name,0);
   }
   public Thread(ThreadGroup group,String name) {
        init(group, null, name,0);
   }
   public Thread(Runnable target,String name) {
        init(null, target,name, 0);
   }
   public Thread(ThreadGroup group,Runnable target,String name) {
        init(group, target,name, 0);
   }
   public Thread(ThreadGroup group,Runnable target,String name,
                  longstackSize) {
        init(group, target,name, stackSize);
   }

   
   public synchronized void start();
   privatenative void start0();


   @Override
   publicvoid run(){
       if (target!= null) {
           target.run();
       }
    }


   private void exit();

   public static boolean interrupted() {
       return currentThread().isInterrupted(true);
   }


   public boolean isInterrupted() {
       return isInterrupted(false);
   }


   private native boolean isInterrupted(booleanClearInterrupted);


   public final native boolean isAlive();

   
   public final void setPriority(intnewPriority);


   public final int getPriority();

   public final synchronized void setName(String name);


   public final StringgetName();

   public final ThreadGroupgetThreadGroup();

   public static int activeCount() {
       return currentThread().getThreadGroup().activeCount();
   }


   public static int enumerate(Thread tarray[]) {
       return currentThread().getThreadGroup().enumerate(tarray);
   }


   public final synchronized void join(longmillis)
   throwsInterruptedException;
   public final synchronized void join(longmillis, intnanos)
   throwsInterruptedException ;

   public final voidjoin() throws InterruptedException {join(0);}

   public final void setDaemon(booleanon);
   public final boolean isDaemon();

   public final void checkAccess();


   public StringtoString() {
        ThreadGroup group =getThreadGroup();
        if(group!= null){
           return "Thread["+ getName() + ","+ getPriority() +"," +
                          group.getName() +"]";
       } else{
           return "Thread["+ getName() + ","+ getPriority() +"," +
                            ""+"]";
       }
    }


   private native static Thread[]getThreads();

   public long getId() {return tid;}

   public enum State {
               NEW,
               RUNNABLE,
               BLOCKED,     
                WAITING,
                TIMED_WAITING,
                TERMINATED;
   }

   public StategetState()

    longthreadLocalRandomSeed;
   intthreadLocalRandomProbe;
   intthreadLocalRandomSecondarySeed;

   /*Some private helper methods */
   privatenative void setPriority0(intnewPriority);
    private native voidstop0(Object o);
    private native voidsuspend0();
    private native voidresume0();
    private native voidinterrupt0();
    private native voidsetNativeName(Stringname);
}

线程的基本使用:

Thread(Runnable target)

//基本构造函数,使用Runnable对象创建一个线程,用于调用target的run()方法

void start();

//启动线程,将引发调用run()方法。 这个方法立即返回, 新线程将并行运行

static void sleep(long millis);

//休眠给定的毫秒数

看下Thread的run()方法:

@Override
public void run() {
   if(target!=null) {
       target.run();
   }
}

可以了解到start()方法启动线程后的大概动作细节:

Start 方法启动线程, 新线程将会运行Thread 对象的run()方法, 进而执行target 对象的run()方法。

所以还有一种定义线程的方式,  通过构建一个Thread 的子类,覆盖run()方法, 该方法已不再推荐使用

14.2 中断线程

没有可以强制终止线程的方法, 然而,interrupt 方法可以用来请求终止线程。

当最一个线程调用interruct方法时,线程的中断状态将被置位。  每个线程都应该不时检查这个标志,以判断线程是否被中断。

被中断的线程可以决定如何响应中断, 不过情况一般都会将终止线程。

public void interrupt()
//调用native methodinterrupt0()将线程中断状置为true;

public static boolean interrupted() {
   returncurrentThread().isInterrupted(true);
}
//静态方法,判断线程是否被中断, 同时将线程中断状态重置为false

public boolean isInterrupted() {
   returnisInterrupted(false);
}
//判断线程是否被中断, 这一调用不改变线程中断状态

public static native Thread currentThread();
//返回当前执行线程的Thread 对象

14.3 线程状态

线程状态有6中,  在Thread 类中就可以找到:

public enum State {
   NEW,
   RUNNABLE,
    BLOCKED,
   WAITING,
   TIMED_WAITING,
   TERMINATED;
}

NEW:
Thread state for a thread that has not yet started.  
使用new新创建的线程,  线程还没开始运行。

  RUNNABLE,
   * Thread state for a runnable thread.  A thread in the runnable
   * state is executing in the Java virtual machine but it may
   * be waiting for other resources from the operating system
   * such as the processor.
   调用start方法后,线程处于Runnable状态。  线程在JVM 中是处于执行状态,  但是否真的在运行由操作系统决定。  
   如果操作系统为线程分配了时间片,线程就在执行。 所以称为Runnable.

Blocked:
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
当线程试图获取一个内部的对象锁,而该锁被其他线程持有, 该线程进入被阻塞状态。


Waiting:
 Thread state for awaiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
*<ul>
*   <li>{@linkObject#wait() Object.wait} with no timeout</li>
*   <li>{@link#join() Thread.join} with no timeout</li>
*   <li>{@linkLockSupport#park() LockSupport.park}</li>
* </ul>
*
*<p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called<tt>Object.wait()</tt>
* on an object iswaiting for another thread to call
*<tt>Object.notify()</tt>or<tt>Object.notifyAll()</tt>on
* that object. A thread that has called<tt>Thread.join()</tt>
* is waiting for aspecified thread to terminate.
当线程等待另一个线程执行某个动作时,该线程进入waiting状态。


Timed_Waiting:
* Thread state for awaiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
*<ul>
*   <li>{@link#sleep Thread.sleep}</li>
*   <li>{@linkObject#wait(long) Object.wait} with timeout</li>
*   <li>{@link#join(long) Thread.join} with timeout</li>
*   <li>{@linkLockSupport#parkNanos LockSupport.parkNanos}</li>
*   <li>{@linkLockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
计时等待是一种特殊的Waiting状态,sleep,wait,join等方法可以有一个超时参数, 这是线程进入计时等待状态


Terminated:
* Thread state for a terminated thread.
* The thread has completed execution.

Join 方法:

public final void join()
//等待终止指定的线程
public finalsynchronized voidjoin(longmillis)
//等待指定的线程死亡或者经过指定的毫秒数
java多线程并发 , java核心编程
java多线程并发–线程状态

14.4 线程属性

14.4.1 优先级

在Java语言中,每一个线程都有优先级。   默认情况下,一个线程继承它的父线程(创建该线程的线程)优先级。

可以将线程优先级定义在MIN_PRIORITY(1)和MAX_PRIORITY(10)之间,  默认优先级为NORM_PRIORITY(5);

每当线程调度器有机会选择新线程时,它首先选择具有较高优先级的线程。 线程优先级是高度依赖操作系统的, 不要将程序的正确性依赖于优先级。

Get 和Set方法用于查看和设置线程优先级

Yield() 静态方法导致当前线程处于让步状态, 如果有其他Runnable线程具有至少与此线程同样高的优先级, 那边这些线程接下来会被调度。

Thread 类中优先级的定义:

private int            priority;
/**
  * The minimum priority that a thread can have.
  */
 public final static int MIN_PRIORITY = 1;

/**
  * The default priority that is assigned to a thread.
  */
 public final static int NORM_PRIORITY = 5;

 /**
  * The maximum priority that a thread can have.
  */
 public final static int MAX_PRIORITY = 10;

 public final void setPriority(intnewPriority)

public final int getPriority() {
   returnpriority;
 }

public static native voidyield()

14.4.2 守护线程

守护线程的作用是为其他线程提供服务,这是一个逻辑上的定义, 实际上和普通线程没有什么区别。

当只剩下守护线程时,虚拟机就退出了

/* Whether or not thethread is a daemon thread. */
private boolean     daemon=false;

public final void setDaemon(booleanon)
//标识一个线程是否为守护线程,必须在线程启动前调用

14.5  同步

14.5.1 竞争条件

竞争条件(race condition): 多线程引起的对共享数据讹误。

竞争条件引起的错误有时很快就出现了,  有时很长时间才会出现, 这样的状态带来不信任感!对程序员来说,没有比无规律出现的错误更糟糕的了!

14.5.2 锁对象

两种机制防止代码块受并发访问的干扰

1.      ReentrantLock

2.      Synchronized 关键字

使用ReentrantLock保护代码块的基本结构如下:

myLock.lock();

try

{

}

Finally

{

    myLock.unlock();

}

解锁操作在finally 字句中是非常重要的,保证如果抛出异常资源被释放,  锁也可以被认为是一种资源。

ReentranLock 是可重入的,线程可以重复获得已持有的锁。锁保持一个持有计数(holdcount)用来跟踪对lock 方法的嵌套调用. 持有计数为0,线程释放锁。

Lock 接口定义如下:

public interface Lock {
   //获得锁,  如果锁被另一个线程持有则发生阻塞
   void lock();

    //获得锁,但是会不确定的发生阻塞。 如果线程被中断,会抛出异常
   void lockInterruptibly()throws InterruptedException;

    //尝试获得锁, 成功立即返回true,否则立即返回false,不会发生阻塞    
   boolean tryLock();

    //尝试获得锁,最长等待时间为time;成功返回true
   boolean tryLock(longtime, TimeUnit unit) throwsInterruptedException;

    //释放这个锁
   void unlock();


        //获得一个与锁相关的条件对象
   Condition newCondition();

ReentrantLock:

public ReentrantLock()
//构建一个可以用来保护临界区的可重入锁

public ReentrantLock(boolean fair)
//构建一个带有公平策略的锁,一个公平锁偏爱等待时间最长的线程,公平的保证会大大降低性能,所以默认情况下锁是没有公平策略的

14.5.3 条件对象

通常线程进入临界区,却发现在某一条件满足后它才能执行。 要使用一个条件对象管理哪些已经获得了一个锁却不能做有用工作的线程。

public interface Condition {

    //将该线程放到条件的等待集中
 //不同的参数使用, await 使用后线程进入Waiting 状态,  如果带有超时参数,线程进入Timed_Waiting状态
   void await()throws InterruptedException;
   void awaitUninterruptibly();
   long awaitNanos(longnanosTimeout) throwsInterruptedException;
   boolean await(longtime, TimeUnit unit) throwsInterruptedException;
   boolean awaitUntil(Date deadline)throws InterruptedException;

    //从该条件的等待集中随机的选择一个线程唤醒
   void signal();
    //唤醒该条件等待集中的所有线程
   void signalAll();
}

等待获得锁的线程和调用await方法的线程存在本质上的不同:

等待获得的锁的线程状态是Locked的, 一旦获得锁线程就可以继续往下执行了。

调用await方法的线程先放弃了锁, 然后线程进入了Waiting状态(或者Timed_Waiting状态), 直到另一线程调用同一条件上的signalAll时,线程被唤醒成为Runnable状态。 然后线程要重新获得锁,  当锁可用时, 从被阻塞的地方继续执行。

通常sinalAll方法仅仅能通知正在等待的线程,  条件可能已满足,值得再次检测, 所以通常await的使用应使用如下形式:

while(!(ok to proceed))

condition.await();

14.5.4 synchronized 关键字

从Java1.0版本开始,Java 中的每一个对象都有一个内部锁。  如果一个方法使用synchronized 关键字,那么对象的锁将保护整个方法。 换句话说,  要调用该方法, 线程必须获得内部的对象锁。

内部对象锁只有一个相关条件。wait 方法添加一个线程到等待集中,notifyAll/notify 方法解除等待线程的Waiting 状态。

将静态方法声明为synchronized 是合法的, 如果调用这个方法,  将获得相应类对象(class对象)的内部锁, 此时其他线程不能调用同一个类的任何静态方法。

14.5.5 同步阻塞

每个Java对象有一个锁, 线程可以通过调用同步方法获得锁。 还可以通过进入如下形式的阻塞来获得锁:

synchronized(obj)

{

……

}

线程将获得obj 对象的内部锁

14.5.6 监视器概念

锁和条件不是面向对象的,监视器是一种面向对象的线程安全方案, 程序员不用考虑如何加锁就保证线程安全。

监视器(monitor)具有如下特性:

  • 1.      监视器是只包含私有域的类;
  • 2.      每个监视器类的对象具有一个相关的锁;
  • 3.      使用该锁对所有的方法加锁;
  • 4.      该锁可以有任意多个相关条件

Java 设计者以不是很精确的方式采用了监视器概念,  Java 中每个对象有一个内部锁和内部条件。  如果方法用synchronized 声明, 表现的像个监视器。

14.5.7 Volatile 域

Volatile 关键词为实例域的同步访问提供了一种免锁机制。 如果声明一个域为volatile,那边编译器和虚拟机就知道该域是可能被另一个线程并发更新的。

Volatile 不能保证原子性。

14.5.8  死锁

所有线程被阻塞,导致程序被挂起的状态,称为死锁

死锁是一种程序设计问题,  语言机制不能避免或打破这种现象。

14.5.9 线程局部变量

可以使用ThreadLocal 辅助类为各个线程提供各自的实例,避免线程间共享变量的风险。

public ThreadLocal()
//创建一个线程局部变量

public T get()
//得到这个线程的当前值, 如果是首次调用get, 会调用initialize 来初始化

protected T initialValue()
//应该覆盖这个方法来提供一个初始值。 默认情况下,这个方法返回null

public void set(Tvalue)
//为这个线程设置一个新值

public void remove()
//删除对应这个线程的值

14.5.10 锁测试与超时

线程直接使用lock方法来获得另一个线程持有的锁对象时, 很可能发生阻塞。

tryLock 方法可以试图去申请一个锁,  成功获得锁后返回true, 否则立即返回flase,  而且线程可以立即离开去做其他事情, 不会发生阻塞

/尝试获得锁, 成功立即返回true,否则立即返回false,不会发生阻塞    
   boolean tryLock();

    //尝试获得锁,最长等待时间为time;成功返回true
   boolean tryLock(longtime, TimeUnit unit) throwsInterruptedException;

    //获得锁,但是会不确定的发生阻塞。 如果线程被中断,会抛出异常
   void lockInterruptibly()throws InterruptedException


if(bankLock.tryLock())
{
   try{
       while(accounts[from] < amount)
           sufficientFunds.await();
       System.out.print(Thread.currentThread());
       accounts[from] -= amount;
       accounts[to] += amount;
       System.out.printf(" %10.2f from %dto %d ",amount,from,to);
       System.out.printf(" Total Balance:%10.2f%n ",getTotalBalance());
       sufficientFunds.signalAll();
   }
   finally{
       bankLock.unlock();
   }
}
else
{
    System.out.println(Thread.currentThread()+"try to get banklock failed, dosomethings else!");
}

14.5.11 读/写锁

public interface ReadWriteLock {
   /**
     * Returns the lock used for reading.
     *
     * @return the lock used forreading
     */

//可以被多个读操作公用的锁, 会排斥所有写操作
   Lock readLock();

   /**
     * Returns the lock used for writing.
     *
     * @return the lock used forwriting
     */


//得到一个写锁,排斥所有其他的读操作和写操作
   Lock writeLock();
}

读写锁的接口定义如上,ReentrantReadWriteLock 类是一个实现类

14.5.12 为什么弃用stop 和suspend方法?

Stop 方法天生不安全,stop 会终止所有未结束的方法, 会破坏对象的状态。

Suspend 会经常导致死锁。

14.6 阻塞队列

对于多线程问题, 可以通过使用一个或者多个队列以优雅安全的方式将其形式化。  生产者线程向队列中插入元素, 消费者线程则取出它们。

(模拟现实中排队系统  银行或者医院的某一门业务员或者医生只能一个一个任务的处理,  同时有很多任务处理怎么办?   以前要排队, 现在要挂号。  挂号就是生产者线程(用户或者病人)向队列中添加任务的过程,  消费者线程(银行柜员或医生)从挂号系统的最前面取出一个任何执行,  先到先得, 先进先出。

BlockQueue 接口定义:

public interface BlockingQueue<E>extends Queue<E> {
   boolean add(Ee);
    //添加一个元素,若队列满,则抛出IllegalStateException

   boolean offer(Ee);
    //添加一个元素并返回true,否则返回false

   boolean offer(Ee, long timeout, TimeUnit unit)
       throws InterruptedException;
    //添加一个元素,若没有空间会等待timeout时间

   void put(Ee) throws InterruptedException;
    //添加一个元素,若队列满则阻塞

E take()throwsInterruptedException;
//移出并返回头元素, 若队列为空则阻塞

E poll(longtimeout, TimeUnit unit)
       throws InterruptedException;
   //移出并返回队列的头元素,若队列为空,会等待timeout 时间


   int remainingCapacity();


   boolean remove(Object o);
   //移出头元素

   public boolean contains(Object o);

   int drainTo(Collection<?superE>c);

   
   int drainTo(Collection<?superE>c, intmaxElements);
}

阻塞队列总主要使用的是put和take方法,  这两个方法在不能达到目的时会阻塞, 达到线程管理的目的。

阻塞队列的使用模拟现实世界的挂号系统,  一般是多个生产者线程,一个消费者线程, 进而保证对某一个数据的同步访问。  若要操作多个数据,  就使用多个队列,  类似银行会有多个服务窗口。

Java.util.concurrent 包中提供了阻塞队列的几种实现和变种:

  • 1.      LinkedBlockingQueue

默认容量没有上边界

  • 2.      LinkedBlockingDeque

双端队列版本

  • 3.      ArrayBlockingQueue

构造时需要指定容量

  • 4.      PriorityBlockingQueue

带有优先级的队列,而不是先进先出

  • 5.      DelayQueue

实现了Delayed 接口

  • 6.      LinkedTranferQueue

实现了TransferQueue 接口

14.7 线程安全的集合

前面一节中的阻塞队列都是线程安全的集合。

  • 1.      ConcurrentLinkedQueue()

     构造一个可以被多线程安全访问的无边界非阻塞的队列

  • 2.      ConcurrentSkipListSet()

构造一个可以被多线程安全访问的有序集

  • 3.      ConcurrentHashMap()

构造一个可以被多线程安全访问的哈希散列映射表

  • 4.      ConcurrentSkipListMap()

构造一个可以被多线程安全访问的有序的映射表

14.8 Callable 与Future

public interface Callable<V> {
   /**
     * Computes a result, or throws anexception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unableto compute a result
     */
   V call()throws Exception;
}

Callable 与Runnable类似,  不同的Runnable 是没有参数和返回值得异步方法

而Callable 有返回值。

public interface Future<V> {
   boolean cancel(booleanmayInterruptIfRunning);
    //尝试取消这一个任务的运行。 如果任务已经开始,并且mayuInterrupt为true, 它就被中断。  如果成功执行了取消操作,返回true.

   boolean isCancelled();

//如果任务在完成前被取消了返回true

   boolean isDone();
    //如果任务结束,无论是正常结束,中途取消或者发生异常,都返回true;

   V get()throws InterruptedException,ExecutionException;
   V get(longtimeout, TimeUnit unit)
       throws InterruptedException,ExecutionException,TimeoutException;
//获取结果,如果没有结果可用,则阻塞直到真正得到结果或者超过指定时间。如果不成功,第二个方法会抛出异常。
}

 

Future 用来保存异步计算的结果。

FutureTask 包装器是一种非常便利的机制, 可以将Callable转换成Future和Runnable

public class FutureTask<V>implements RunnableFuture<V>
public FutureTask(Callable<V> callable)
public FutureTask(Runnable runnable,V result)
//构造一个即时Future<V>有是Runnable的对象

14.9 执行器

如果程序中创建了大量生命周期很短的线程,应该使用线程池。  一个线程池包含很多准备运行的线程,将Runnable 对象线程池,就会有一个线程调用run方法。  当run 方法退出时,线程不会死亡,  而是在池中等待为下一个请求提供服务。

线程池也运用了前面的生产者消费者思想。

线程池的实现比较简单, 核心的想法就是线程池中的线程自己的run 方法永不会退出,  然后从任务队列中获取任务r,  运行r.run()来执行任务, 下面是自己实现的一个简单的线程池:

public class Pool {

   privateint poolSize;

   BlockingQueue<Runnable>tasks;

   List<Thread>threads;

   /**
     * Construct a Thread pool
     * @param poolSize
    */
   public Pool(intpoolSize) {
        this.poolSize= poolSize;
       tasks =new LinkedBlockingQueue<>();
       threads =new ArrayList<>();
       startPool();
   }

   /**
     *向线程池中添加任务
     * @param r
    */
   public void addTask(Runnable r)
    {
       tasks.add(r);
   }

   /**
     *启动线程池(中的线程)
     */
   public void startPool()
    {
       for(inti =0 ; i< poolSize;i++)
        {
            Thread t =new Thread(newPoolThread(tasks));
           threads.add(t);
           t.start();
       }
    }

}


/**
 *线程池使用线程, 自己的run 要不能结束,要一直存活
 *获取到Runnable任务后, 显式的调用任务的Run方法来执行任务
 */
class PoolThreadimplementsRunnable
{
   private  BlockingQueue<Runnable>tasks;

    publicPoolThread(BlockingQueue<Runnable>tasks) {
       this.tasks= tasks;
   }

   @Override
   publicvoid run(){
        System.out.println(Thread.currentThread()+"Starts!");
        while(true)
        {
           try
           {
                Runnable r =tasks.take();
               r.run();
           } catch(InterruptedException e)
            {
                e.printStackTrace();
           }
        }
    }
}

当然,线程池怎么管理任务就有比较多的实现了,  这里使用的是阻塞队列,  也可以使用一个简单的List 来存放任务,  通过锁来保护。

当然上面实现的线程池比较简单,  Java 的执行器(Executor) 类提供了强大的线程池支持。

Executors 类提供了很多方法来构建线程池:

public static ExecutorService newCachedThreadPool() {
   returnnew ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                 60L, TimeUnit.SECONDS,
                                  newSynchronousQueue<Runnable>());
}
//返回一个带缓存的线程池,  该池在必要的时候创建线程,在线程空闲60 s后终止线程

public static ExecutorService newFixedThreadPool(int nThreads) {
   returnnew ThreadPoolExecutor(nThreads, nThreads,
                                 0L, TimeUnit.MILLISECONDS,
                                  newLinkedBlockingQueue<Runnable>());
}
//返回一个线程池,该池中的线程数由参数指定

public static ExecutorService newSingleThreadExecutor() {
   returnnew FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                               0L, TimeUnit.MILLISECONDS,
                                newLinkedBlockingQueue<Runnable>()));
}
//返回一个执行器,  它在一个单个线程中依次执行各个任务


public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
//返回一个线程池,它使用给定的线程数来调度任务

public static ScheduledExecutorService newSingleThreadScheduledExecutor()
/返回一个执行器,它在一个单独的线程中调度任务

14.9.1 ExcutorService 定义

public interface ExecutorService extends Executor {

    voidshutdown();
   //关闭服务,会完成已提交的任何而不再接收新的任务

   List<Runnable> shutdownNow();

//取消尚未开始的任务并试图终端正在运行的线程,  返回从未执行的任务列表

   boolean isShutdown();

//判断线程池是否已关闭

   boolean isTerminated();

//判断所有任务是否都已terminated 的,在线程池shut down 后,必须在shutdown 后使用


   boolean awaitTermination(long timeout, TimeUnit unit)
       throws InterruptedException;


   <T> Future<T> submit(Callable<T> task);
   <T> Future<T> submit(Runnable task, T result);
   Future<?> submit(Runnable task);

//提交指定的任务去执行


   <T> List<Future<T>> invokeAll(Collection<? extendsCallable<T>> tasks)
       throws InterruptedException;
   <T> List<Future<T>> invokeAll(Collection<? extendsCallable<T>> tasks,
                                  longtimeout, TimeUnit unit)
       throws InterruptedException;
    //执行给定的任务,返回所有任务的结果。


  <T> T invokeAny(Collection<? extends Callable<T>> tasks)
       throws InterruptedException, ExecutionException;
   <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    longtimeout, TimeUnit unit)
       throws InterruptedException, ExecutionException, TimeoutException;

//执行给定的任务,返回其中一个任务的结果
}

14.9.2 预定执行

ScheduledExecutorService 定义(用来实现定时或者周期性任务):

public interface ScheduledExecutorService extendsExecutorService {

   public ScheduledFuture<?>schedule(Runnablecommand,
                                      longdelay, TimeUnit unit);
   public <V>ScheduledFuture<V> schedule(Callable<V> callable,
                                          longdelay, TimeUnit unit);

// 预定在指定时间后执行的任务

 
   public ScheduledFuture<?>scheduleAtFixedRate(Runnable command,
                                                  longinitialDelay,
                                                 longperiod,
                                                 TimeUnit unit);
   //预定在初始的延迟结束后,周期性的运行给定的任务,周期长度是period

   public ScheduledFuture<?>scheduleWithFixedDelay(Runnable command,
                                                    longinitialDelay,
                                                     longdelay,
                                                    TimeUnit unit);

//预定在初始的延迟结束后周期性给定的任务,在一次调用完成和下一次调用开始之间有长度为delay 的延迟

}

14.9.3 控制任务组

14.9.4 Fork-Join 框架

Fork-join 专门用于计算密集型任务, 对每个处理器内核分别使用一个线程。

14.10 同步器

  • 1.      CyclicBarrier
  • 2.      CountDownLatch
  • 3.      Exchanger
  • 4.      Semaphore
  • 5.      SynchronousQueue

FYI
  多线程可能变得非常复杂,  更加系统学习可以参考”JavaConcurrency in Practice

总结

多线程是多任务系统的一种实现,具有很多参考意义。 java多线程并发机制非常完善。

一个多任务系统总, 任务应该有几种状态(参考线程状态), 有优先级区别等待属性。  多任务系统中要解决的核心问题是多个任务对某一排他性资源(参考需要同步访问的变量)的竞争问题。  现实中的排队系统给了一个很好的参考,  所以我们有了阻塞队列这样一种解决方式。   锁是另一种方案, 不过没有队列那么优雅。

另外任务调度也是一个比较通用的问题,   十字路口的绿灯也是一种调度系统,  和CPU 调度进程,线程 的原理一样,  都是将时间分片,  然后轮流的分配给任务。 这样在某一特定的时刻只有一个任务在运行(某一个特定的时刻, 只有一个方向是通行的)。  但总体看起来是多任务并行。  

java多线程并发 , java核心编程 的相关内容到这里就结束啦, 更多关于Java的内容可以参考我的Java专栏

1 thought on “Core Java Volume I 读书笔记—第十四章 多线程”

  1. Pingback: Core Java Volume Java核心编程读书笔记 – The Hu Post

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top