多线程的三种实现

来源:互联网 发布:maven java 7 home 编辑:程序博客网 时间:2024/05/13 14:24

    最近写多篇关于多线程的博客,这里做个总结。线程从创建到消亡的过程中,可能会经历五种状态:

  1. New:线程刚被创建。
  2. Runnable:线程的start方法被调用后所处的状态,该状态下,线程才有了竞争时间片的可能,即可运行态。
  3. Running:线程正在执行run方法
  4. Dead:执行完run方法,或者被stop
  5. Blocked:线程放弃CPU使用权,进入阻塞状态。该状态下,线程可能重新被赋予CPU使用权进入Runnable状态,也可能直接消亡。阻塞状态可以细分成三种:
  • 等待阻塞:Runnable状态的的中运行Object的wait方法,被JVM放入等待池。
  • 同步阻塞:线程竞争同步锁失败,被JVM放入锁池。
  • 其他:线程执行sleep、Join等方法,被JVM置为阻塞状态,等待JVM重置状态为Runnable。

    Java中实现多线程的方式主要有三种:继承Thread类、实现Runnable接口与使用线程池。

继承Thread类与实现Runnable接口
    需要重写Thread类的run方法,或者实现Runnable的run方法,通过调用线程的start方法,启动线程。直接上例子:
package test.thread;import java.util.LinkedList;import java.util.Queue;public class ThreadTest1 {public static void main(String[] args) {final Queue<Integer> queue = new LinkedList<Integer>();for (int i = 0; i < 1000; i++) {queue.add(i);}Thread[] threads = new Thread[10];for (int i = 0; i < 10; i++) {threads[i] = new Thread(new Runnable() {@Overridepublic void run() {synchronized (queue) {try {queue.wait();} catch (InterruptedException e) {e.printStackTrace();}for (int j = 0; j < 100; j++) {System.out.println(Thread.currentThread().getName() + ": " + queue.poll());}}}});threads[i].start();}try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}synchronized (queue) {queue.notifyAll();}}}
    上面例子,在主线程与10个子线程中都用到了共享变量queue。通过调用queue的wait方法阻塞子线程,等待主线程调用notifyAll方法时,重新进入Runnable状态,保证了线程安全。需要注意object的wait、notify、notifyAll等方法需要放在同步块中。
    同样的功能,我们也可以这样实现:
package test.thread;import java.util.LinkedList;import java.util.Queue;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class ThreadTest2 {public static void main(String[] args) {final Lock lock = new ReentrantLock();final Condition condition = lock.newCondition();final Queue<Integer> queue = new LinkedList<Integer>();for (int i = 0; i < 1000; i++) {queue.add(i);}Thread[] threads = new Thread[10];for (int i = 0; i < 10; i++) {threads[i] = new Thread(new Runnable() {@Overridepublic void run() {lock.lock();try {condition.await();for (int j = 0; j < 100; j++) {System.out.println(Thread.currentThread().getName() + ": " + queue.poll());}} catch (InterruptedException e1) {e1.printStackTrace();} finally {lock.unlock();}}});threads[i].start();}try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}lock.lock();try {condition.signalAll();} finally {lock.unlock();}}}
    上面例子用到了ReentrantLock,相比synchronized关键字提供更加精细的同步锁功能。使用时需要注意要把锁的unlock方法,置于finally块中,避免线程异常中断,锁不被释放。
    我们还可以这样实现:
package test.thread;import java.util.LinkedList;import java.util.Queue;import java.util.concurrent.Semaphore;public class ThreadTest3 {public static void main(String[] args) {final Semaphore semaphore = new Semaphore(1);final Queue<Integer> queue = new LinkedList<Integer>();for (int i = 0; i < 1000; i++) {queue.add(i);}Thread[] threads = new Thread[10];for (int i = 0; i < 10; i++) {threads[i] = new Thread(new Runnable() {@Overridepublic void run() {try {semaphore.acquire(1);for (int j = 0; j < 100; j++) {System.out.println(Thread.currentThread().getName() + ": " + queue.poll());}} catch (Exception e1) {e1.printStackTrace();} finally {semaphore.release(1);}}});threads[i].start();}}}
    这个例子中,我们用到了信号量。当线程获得semaphore时,如果semaphore的内部计数值大于0,就会减少计数值并允许访问共享资源。
使用线程池
    实现线程池主要通过Executors工具类以及ThreadPoolExecutor线程池实现类。ThreadPoolExecutor是线程池的核心功能,这里先挖个坑,后面有时间再整理。Executors提供了四种线程池:
newCachedThreadPool

    该方法创建一个可缓存的线程。线程池无线大,可以复用已经执行完璧的线程。

newScheduledThreadPool

    该方法创建一个定长的线程。可以实现并发线程个数,超过并发线程个数的线程进入等待队列。

newScheduledThreadPool

    该方法创建一个定长的线程,支持周期性任务的执行。

newSingleThreadExecutor

    该方法创建一个单线程化的线程池,同一时间只有一个线程在执行任务。

0 0
原创粉丝点击