亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Java并發(fā)編程之多線程和線程池

wums / 527人閱讀

摘要:目標線程由運行狀態(tài)轉(zhuǎn)換為就緒狀態(tài),也就是讓出執(zhí)行權(quán)限,讓其他線程得以優(yōu)先執(zhí)行,但其他線程能否優(yōu)先執(zhí)行時未知的。函數(shù)的官方解釋是意思是使調(diào)用該函數(shù)的線程讓出執(zhí)行時間給其他已就緒狀態(tài)的線程。

線程允許在同一個進程中同時存在多個程序控制流,即通過線程可以實現(xiàn)同時處理多個任務(wù)的功能。線程會共享進程范圍內(nèi)的資源,例如內(nèi)存句柄和文件句柄,但每個線程都有各自的程序計數(shù)器、棧以及局部變量。

多線程的實現(xiàn) 實現(xiàn)方式

對于Java的多線程來說,我們學習的一般都是Thread和Runnable,通過我們使用如下代碼啟動一個新的線程:

private void startewThread() {

    new Thread(){

        @Override
        public void run() {

            // 耗時任務(wù)
        }

    }.start();
}
或者
private void startewThread1(){

    new Thread(new Runnable() {

        @Override
        public void run() {
            // 耗時任務(wù)

        }
    }).start();
}

第一種是覆寫了Thread類中的run方法執(zhí)行任務(wù);第二種是實現(xiàn)Runnable接口中的run方法執(zhí)行任務(wù)。

那么Thread和Runnable是什么關(guān)系呢?

Thread和Runnable的關(guān)系

實際上Thread也是一個Runnable,它實現(xiàn)了Runnable接口,在Thread類中有一個Runnable類型的target字段,代表要被執(zhí)行在這個子線程的任務(wù)。相關(guān)代碼如下:

public class Thread implements Runnable {

    //要執(zhí)行的目標任務(wù)
    private Runnable target;
    //線程所屬的線程組
    private ThreadGroup group;
    
    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }
    
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name.toCharArray();

        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            /* Determine if it"s an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }

            /group為null則獲取當前線程的線程組
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();

        /*
         * Do we have the required permissions?
         */
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();

        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        //設(shè)置target
        this.target = target;
        setPriority(priority);
        if (parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }
    
   public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group"s list of threads
         * and the group"s unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            //調(diào)用native函數(shù)啟動線程
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
    
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}

實際上最終被線程執(zhí)行的任務(wù)是Runnable,而非Thread。Thread 只是對Runnable的包裝,并且通過一些狀態(tài)對Thread進行管理和調(diào)度。Runnable的聲明如下:

public interface Runnable {

    public void run();

}

當啟動一個線程時,如果Thread的target不為空,則會在子線程中執(zhí)行這個target的run方法,否則虛擬機就會執(zhí)行該線程自身的run方法。

線程的wait、sleep、join和yield

先通過下面的表格來了解他們的區(qū)別:

函數(shù)名 作用
wait 當一個線程執(zhí)行到wait()方法時,它就進入到一個和該對象相關(guān)的等待池中,同時釋放了對象的鎖,使得其他線程可以訪問。用戶可以使用notify、notifyAll或指定睡眠時間來喚醒當前等待池中的線程。 注意:wait、notify、notifyAll方法必須放在synchronized block中,否則則會拋出異常。
sleep 該函數(shù)時Thread的靜態(tài)函數(shù),作用是使調(diào)用線程進入睡眠狀態(tài)。因為sleep()Thread的靜態(tài)函數(shù),因此它不能改變對象的鎖。所以當一個synchronized塊中調(diào)用sleep方法時,線程雖然休眠了,但是對象的鎖并沒有被釋放,其他線程無法訪問這個對象(即使睡著也持有對象鎖)
join 等待目標線程執(zhí)行完成之后再繼續(xù)執(zhí)行
yield 線程禮讓。目標線程由運行狀態(tài)轉(zhuǎn)換為就緒狀態(tài),也就是讓出執(zhí)行權(quán)限,讓其他線程得以優(yōu)先執(zhí)行,但其他線程能否優(yōu)先執(zhí)行時未知的。
wait()

下面來看看wait、notify、notifyAll的使用:

public class WaitDemo {

    private static Object lockObject = new Object();

    private static void waitAndNotifAll() {

        System.out.println("主線程運行");

        //創(chuàng)建并啟動子線程
        Thread thread = new WaitThread();
        thread.start();

        long startTime = System.currentTimeMillis();

        try {
            //必須在synchronized塊中
            synchronized (lockObject) {
                System.out.println("主線程等待");
                lockObject.wait();

            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //被喚醒后繼續(xù)執(zhí)行
        long endTime = System.currentTimeMillis() - startTime;
        
        System.out.println("主線程繼續(xù)--->等待耗時: " + endTime + "ms");

    }

    private static class WaitThread extends Thread {

        @Override
        public void run() {
            // TODO Auto-generated method stub
            synchronized (lockObject) {
                try {
                    Thread.sleep(3000);
                    //喚醒正在等待中的線程
                    lockObject.notifyAll();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }
        }
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        waitAndNotifAll();

    }

}

運行結(jié)果:

主線程運行
主線程等待
...
...

主線程繼續(xù)--->等待耗時: 3001ms

wait、notify機制通常用于等待機制的實現(xiàn),當條件未滿足時調(diào)用wait進入等待狀態(tài),一旦條件滿足,調(diào)用notify或notifyAll喚醒等待的線程繼續(xù)執(zhí)行。

join()

join函數(shù)的原始解釋為“Block the cuurent thread(Thread.currentThread()) untile the receiver finishes its execution and dies。意思就是阻塞當前調(diào)用join函數(shù)的任務(wù)所在的線程,直到該任務(wù)執(zhí)行完成后再繼續(xù)執(zhí)行所在線程的任務(wù)。下面我們來看看一個具體是實例:

public class JoinDemo {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        joinDemo();
    }

    static void joinDemo() {

        System.out.println("主線程開始執(zhí)行");
        
        Worker worker1 = new Worker("worker-1");
        Worker worker2 = new Worker("worker-2");

        worker1.start();

        System.out.println("啟動線程1--執(zhí)行完畢");

        try {
            //等待worker1任務(wù)執(zhí)行完成
            worker1.join();
            
            System.out.println("啟動線程2--執(zhí)行完畢");
            
            worker2.start();
            
            //等待worker2任務(wù)執(zhí)行完成
            worker2.join();
            
            
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        System.out.println("主線程繼續(xù)執(zhí)行");
        
        System.out.println("主線程執(zhí)行完畢");
    }

    static class Worker extends Thread {

        public Worker(String name) {
            super(name);
        }

        @Override
        public void run() {
            // TODO Auto-generated method stub
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            System.out.println("Work in " + getName());
        }
    }

}

結(jié)果打?。?
主線程開始執(zhí)行
啟動線程1--執(zhí)行完畢
Work in worker-1
啟動線程2--執(zhí)行完畢
Work in worker-2
主線程繼續(xù)執(zhí)行
主線程執(zhí)行完畢

上述代碼的邏輯是主線程開始執(zhí)行、啟動線程1、等待線程1執(zhí)行完畢、啟動線程2、等待線程2執(zhí)行完畢、繼續(xù)執(zhí)行主線程任務(wù)。

yield()
public static native void yield();

yield函數(shù)的官方解釋是"Causes the calling Thread to yiled execution time to another Thread that is ready to run",意思是使調(diào)用該函數(shù)的線程讓出執(zhí)行時間給其他已就緒狀態(tài)的線程。

線程的執(zhí)行是有時間片的,每個線程輪流占用CPU固定的時間,執(zhí)行周期到了之后就讓出執(zhí)行權(quán)給其他線程,而yield函數(shù)的功能就是主動讓出線程的執(zhí)行權(quán)給其他線程,其他線程能否得到優(yōu)先權(quán)就得看各個線程的狀態(tài)了。下面來看看一個具體的示例:

public class YieldDemo {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        YieldThread t1 = new YieldThread("thread-1");
        YieldThread t2 = new YieldThread("thread-2");
        t1.start();
        t2.start();

    }

    
    static class YieldThread extends Thread {
        
        public YieldThread(String name) {
            // TODO Auto-generated constructor stub
            super(name);
        }
        
        @Override
        public synchronized void run() {
            // TODO Auto-generated method stub
            for(int i = 0; i < 5;i++) {
                System.out.println(this.getName() + " ; " + "線程優(yōu)先級為: " + this.getPriority()+ "--->" + i);
                
                //當i為2時 調(diào)用當前線程yield函數(shù)
                if (i== 2) {
                    Thread.yield();
                }
            }
        }
    }
}

打印結(jié)果:

thread-1 ; 線程優(yōu)先級為: 5--->0
thread-2 ; 線程優(yōu)先級為: 5--->0
thread-2 ; 線程優(yōu)先級為: 5--->1
thread-2 ; 線程優(yōu)先級為: 5--->2
thread-1 ; 線程優(yōu)先級為: 5--->1
thread-1 ; 線程優(yōu)先級為: 5--->2
thread-2 ; 線程優(yōu)先級為: 5--->3
thread-2 ; 線程優(yōu)先級為: 5--->4
thread-1 ; 線程優(yōu)先級為: 5--->3
thread-1 ; 線程優(yōu)先級為: 5--->4

從結(jié)果可知,thread-2首先執(zhí)行到i的值為2,此時讓出執(zhí)行權(quán),thread-1得到執(zhí)行權(quán)運行到i的值為2時讓出執(zhí)行權(quán),thread-2得到執(zhí)行權(quán)執(zhí)行任務(wù)結(jié)束,然后thread-1再繼續(xù)執(zhí)行任務(wù)。

注意:yield僅在一個時間片內(nèi)有效。

Callable、Future和FutureTask

除了Runnable之外,Java還有Callable、Future和FutureTask這幾個與多線程相關(guān)的概念,與Runnable不同的是這個類型都只能運用到線程池中,而Runnable既能運用在Thread中,還能運用在線程池中。

Callable

Callable與Runnable的功能大致相似不同的是Callable是一個泛型接口,它有一個泛型參數(shù)V,該接口中有一個返回值(類型為V)的Call函數(shù),而Runnable中的run方法不能將結(jié)果返回至調(diào)用者。Callable的聲明如下:

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

Future為線程池制定了一個可管理的任務(wù)標準。它提供了對Runnable或者Callable任務(wù)的執(zhí)行結(jié)果進行取消、查詢是否完成、獲取結(jié)果、設(shè)置結(jié)果操作,分別對應(yīng)cancel、isDone、get、set函數(shù)。get方法會阻塞,直到任務(wù)返回結(jié)果。Future的聲明如下:

public interface Future {

    //取消任務(wù)
    boolean cancel(boolean mayInterruptIfRunning);

    //判斷任務(wù)是否已經(jīng)取消
    boolean isCancelled();

    //判斷任務(wù)是否已經(jīng)完成
    boolean isDone();

    //獲取結(jié)果,如果任務(wù)未完成則等待,直到完成,因此該函數(shù)會阻塞
    V get() throws InterruptedException, ExecutionException;

    //獲取結(jié)果,如果未完成則等待,直到返回結(jié)果或timeout,該函數(shù)會阻塞
    V get(long timeout, TimeUnit unit)
    throws InterruptedException, ExecutionException, TimeoutException;
}
FutureTask

Future只是定義了一些規(guī)范的接口,而FutureTask則是它的實現(xiàn)類。FutureTask實現(xiàn)了RunnableFuture,而RunnableFuture實現(xiàn)了Runnable又實現(xiàn)了Future這兩個接口,因此FutureTask同時具備他們的功能。FutureTask的代碼如下:

public class FutureTask implements RunnableFuture {
    .....
}

RunnableFuture類的定義

public interface RunnableFuture extends Runnable, Future {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

FutureTask像Thread那樣包裝Runnable那樣對Runnable和Callable進行包裝,Runnable與Callable由構(gòu)造函數(shù)注入

public FutureTask(Callable callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}

public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}

從上述代碼可以看出,如果注入的是Runnable則會被Executors.callable()函數(shù)轉(zhuǎn)換為Callable類型,即FutureTask最終都是執(zhí)行Callable類型的任務(wù),該轉(zhuǎn)換函數(shù)如下:

public static  Callable callable(Runnable task, T result) {
    if (task == null)
        throw new NullPointerException();
    return new RunnableAdapter(task, result);
}

/**
* Runnable適配器,將Runnable轉(zhuǎn)換為Callable
*/
static final class RunnableAdapter implements Callable {
    final Runnable task;
    final T result;
    RunnableAdapter(Runnable task, T result) {
        this.task = task;
        this.result = result;
    }
    public T call() {
        task.run();
        return result;
    }
}

由于FutureTask實現(xiàn)了Runnable,因此它既可以通過Thread包裝來執(zhí)行,也可以提交給ExecuteService來執(zhí)行,并且還可以通過get()函數(shù)來獲取執(zhí)行結(jié)果,該函數(shù)會阻塞,直到結(jié)果返回。因此,F(xiàn)utureTask既是Future、Runnable,又是包裝了Callable(Runnable最終也會被轉(zhuǎn)換為Callable),它是這兩者的合體。

下面示例演示Runnable、Callable、FutureTask的運用,代碼如下:

public class FutureTaskDemo {

    //線程池
    static ExecutorService mExecutor = Executors.newSingleThreadExecutor();
    
    /**
     * 向線程池提交Runnable對象
     */
    private static void taskRunnable() {
        
        //無返回值
        Future future = mExecutor.submit(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                fibc(20);
                
            }
        });

        System.out.println("taskRunnable: " + future.get());
    }
    
    /**
     * 向線程池提交Callable對象
     * @throws ExecutionException 
     * @throws InterruptedException 
     */    
    private static void taskCallable() throws InterruptedException, ExecutionException {
        
        Future future = mExecutor.submit(new Callable() {

            @Override
            public Integer call() throws Exception {
                // TODO Auto-generated method stub
                return fibc(20);
            }
        });
        
        //返回值
        Integer result = future.get();
        
        if (result != null) {
            System.out.println("taskCallable: " + result);
        }
    }

    /**
     * 向線程池提交FutureTask對象
     * @throws ExecutionException 
     * @throws InterruptedException 
     */
    private static void taskFutureTask() throws InterruptedException, ExecutionException {
        
        FutureTask futureTask = new FutureTask<>(new Callable() {

            @Override
            public Integer call() throws Exception {
                // TODO Auto-generated method stub
                return fibc(20);
            }
        });
        
        mExecutor.submit(futureTask);
        
        Integer result = futureTask.get();
        
        if (result != null) {
            System.out.println("taskFutureTask: " + result);
        }
        
    }
    
    /**
     * Thread包裝FutureTask
     * @throws InterruptedException
     * @throws ExecutionException
     */
    private static void taskThread() throws InterruptedException, ExecutionException {
        
        FutureTask futureTask = new FutureTask<>(new Callable() {

            @Override
            public Integer call() throws Exception {
                // TODO Auto-generated method stub
                return fibc(20);
            }
        });
        
        new Thread(futureTask).start();
        
        Integer result = futureTask.get();
        
        if (result != null) {
            System.out.println("taskThread: " + result);
        }
        
    }
    
    /**
     * 斐波那契數(shù)列
     * @param num
     * @return
     */
    private static int fibc(int num) {
        
        if (num == 0) {
            return 0;
        }
        
        if (num == 1) {
            return 1;
        }
        
        return fibc(num - 1) + fibc(num - 2);
    }
    
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        try {
            taskRunnable();
            taskCallable();
            taskFutureTask();
            taskThread();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 

    }

}

打印結(jié)果:

taskRunnable: null
taskCallable: 6765
taskFutureTask: 6765
taskThread: 6765
線程池

當我們需要頻繁地創(chuàng)建多個線程進行耗時操作時,每次都通過new Thread實現(xiàn)并不是一種好的方式,每次new Thread新建銷毀對象性能較差,線程缺乏統(tǒng)一的管理,可能會無限制地創(chuàng)建新的線程,線程之間相互競爭從而占用過多系統(tǒng)資源導(dǎo)致死鎖,并且缺乏定期執(zhí)行、定時執(zhí)行、線程中斷等功能。

Java提供了4中線程池,它能夠有效地管理、調(diào)度線程,避免過多的資源消耗,它強大到幾乎不需要開發(fā)人員自定義的程序。它的優(yōu)點如下:

重用存在的線程,減少對象創(chuàng)建、銷毀的開銷;

可有效控制最大并發(fā)線程數(shù),提高系統(tǒng)資源的使用率,同時避免過多資源競爭,避免堵塞;

提供定時執(zhí)行、定期執(zhí)行、單線程、并發(fā)數(shù)控制等功能;

線程池的原理就是會創(chuàng)建創(chuàng)建多個線程并且對這些線程進行管理,提交給線程的任務(wù) 會被線程池指派給其中的線程執(zhí)行,提供線程池的統(tǒng)一調(diào)度、管理。使得多線程的使用更簡單、高效。

線程池都實現(xiàn)了ExecutorService接口,該接口定義了線程池需要實現(xiàn)的接口,如submit、execute、shutdown等。它的實現(xiàn)有ThreadPoolExecutor和ScheduledPoolExecutor,ThreadPoolExecutor是運行最多的線程池實現(xiàn),ScheduledPoolExecutor則用于執(zhí)行周期性任務(wù)。

啟動指定數(shù)量的線程-ThreadPoolExecutor

ThreadPoolExecutor的功能是啟動指定數(shù)量的線程以及將任務(wù)添加到一個隊列中,并且將任務(wù)分發(fā)給空閑的線程。

ExecutorService的生命周期包括3中狀態(tài):運行、關(guān)閉、終止,創(chuàng)建后進入運行狀態(tài),調(diào)用shutdown()方法時便進入了關(guān)閉狀態(tài),此時ExecutorService不再接受新的任務(wù),但它繼續(xù)執(zhí)行完已經(jīng)提交的任務(wù),當所有已經(jīng)提交的任務(wù)都執(zhí)行完后,就變成終止狀態(tài)。

ThreadPoolExecutor的構(gòu)造函數(shù)如下:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

下面對參數(shù)進行詳細說明:

參 數(shù) 名 作 用
corePoolSize 線程池中所保存的核心線程數(shù)。
maximumPoolSize 線程池所容納的最大線程數(shù),當活動線程達到這個數(shù)值后,后續(xù)的任務(wù)將會被阻塞
keepAliveTime 非核心線程閑置時的超時時間,超出這個時長,非核心線程就會被回收
unit 用于指定keepAliveTime參數(shù)的時間單位,有毫秒、秒、分鐘等
workQueue 線程池中的任務(wù)隊列,如果線程池的線程數(shù)量已經(jīng)達到核心線程數(shù)并且當前所有線程都處于活動狀態(tài)時,則將新任務(wù)放到此隊列中等待執(zhí)行
threadFactory 線程工廠,為線程池提供創(chuàng)建新線程的功能,通常不需要設(shè)置
handler 拒絕策略,當線程池與workQueue隊列都滿了的情況下,對新任務(wù)采取的處理策略

線程池參數(shù)也可以參考這篇文章http://liuguoquan727.github.io/2016/04/25/Android%E7%9A%84%E7%BA%BF%E7%A8%8B%E5%92%8C%E7%BA%BF%E7%A8%8B%E6%B1%A0/:

其中workQueue有下列幾個常用的實現(xiàn):

ArrayBlockingQueue

基于數(shù)組結(jié)構(gòu)的有界隊列,此隊列按FIFO原則對任務(wù)進行排序。如果隊列滿了還有任務(wù)進來,則調(diào)用拒絕策略

LinkedBlockingQueue

基于鏈表結(jié)構(gòu)的無界隊列,此隊列按FIFO原則對任務(wù)進行排序。因為它是無界的,所以才有此隊列后線程池將忽略handler參數(shù)。

SynchronousQueue

直接將任務(wù)提交給線程而不是將它加入到隊列,實際上該隊列是空的。每個插入的操作必須等到另一個調(diào)用移除的操作,如果新任務(wù)來了線程池沒有任何可用線程處理的話,則調(diào)用拒絕策略。

PriorityBlockingQueue

具有優(yōu)先級的隊列的有界隊列,可用自定義優(yōu)先級,默認是按自然排序的。

此外,當線程池與workQueue隊列都滿了的情況下,對新加任務(wù)采取的處理策略也有幾個默認實現(xiàn):

AbortPolicy

拒絕任務(wù),拋出RejectedExecutionException異常,線程池默認策略

CallerRunsPolicy

拒絕新任務(wù)加入,如果該線程池還沒有被關(guān)閉,那么將這個新任務(wù)執(zhí)行在調(diào)用線程中

DiscardOldestPolicy

如果執(zhí)行程序還沒有關(guān)閉,則將位于工作隊列頭部的任務(wù)刪除,然后重試執(zhí)行程序(如果再次失敗,則重復(fù)此過程)

DiscardPolicy

加不進的任務(wù)都被拋棄了,同時沒有異常拋出

newFixedThreadPool

對應(yīng)Android平臺來說,最常使用的就是通過Executors.newFixedThreadPool(int size)函數(shù)來啟動固定數(shù)量的線程池,代碼如下

public class ExectorsDemo {

    private static final int MAX = 10;
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        try {
            fixedThreadPool(MAX);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
    
    private static void fixedThreadPool(int size) throws InterruptedException, ExecutionException {
        
        ExecutorService service = Executors.newFixedThreadPool(size);
        
        for(int i = 0;i < MAX;i++) {
            
            //提交任務(wù)
            Future task = service.submit(new Callable() {

                @Override
                public Integer call() throws Exception {
                    // TODO Auto-generated method stub
                    System.out.println("執(zhí)行線程: " + Thread.currentThread().getName());
                    return fibc(20);
                }
            });
            
            //獲取結(jié)果
            System.out.println("第"+i+"次計算結(jié)果: " + task.get());
        }
    }
    
    /**
     * 斐波那契數(shù)列
     * @param num
     * @return
     */
    private static int fibc(int num) {
        
        if (num == 0) {
            return 0;
        }
        
        if (num == 1) {
            return 1;
        }
        
        return fibc(num - 1) + fibc(num - 2);
    }

}

結(jié)果打?。?
執(zhí)行線程: pool-1-thread-1
第0次計算結(jié)果: 6765
執(zhí)行線程: pool-1-thread-2
第1次計算結(jié)果: 6765
執(zhí)行線程: pool-1-thread-3
第2次計算結(jié)果: 6765
執(zhí)行線程: pool-1-thread-1
第3次計算結(jié)果: 6765
執(zhí)行線程: pool-1-thread-2
第4次計算結(jié)果: 6765
執(zhí)行線程: pool-1-thread-3
第5次計算結(jié)果: 6765
執(zhí)行線程: pool-1-thread-1
第6次計算結(jié)果: 6765
執(zhí)行線程: pool-1-thread-2
第7次計算結(jié)果: 6765
執(zhí)行線程: pool-1-thread-3
第8次計算結(jié)果: 6765
執(zhí)行線程: pool-1-thread-1
第9次計算結(jié)果: 6765

在上述例子中,我們啟動了含有3個線程的線程池,調(diào)用的是Executors的newFixedThreadPool函數(shù),該函數(shù)的實現(xiàn)為

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue());
    }

可知它的corePoolSize和MaxnumPoolSize值都是nThreads,并且設(shè)置keepAliveTime為0毫秒,最后設(shè)置無界任務(wù)隊列,這樣該線程池中就含有固定個數(shù)的線程,并且能夠容納無數(shù)個任務(wù)。

newCacheThreadPool

有時可能需要任務(wù)盡可能快地被執(zhí)行,這就需要線程池中的線程足夠多也就是說此時需要拿空間來換時間,線程越多占用的內(nèi)存消耗就越大。因此,我們可能需要一種場景,如果來了一個新的任務(wù),并且沒有空閑線程可用,此時必須馬上創(chuàng)建一個線程來立即執(zhí)行任務(wù)。我們可以通過Executors的newCacheThreadPool函數(shù)來實現(xiàn)。

private static void newCacheThreadPool() throws InterruptedException, ExecutionException {

    ExecutorService service = Executors.newCachedThreadPool();

    for(int i = 0;i < MAX;i++) {

        //提交任務(wù)
        service.submit(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                System.out.println("執(zhí)行線程: " + Thread.currentThread().getName() + ",結(jié)果:" + fibc(20));
            }
        });

    }
}
結(jié)果打印

執(zhí)行線程: pool-1-thread-1,結(jié)果:6765
執(zhí)行線程: pool-1-thread-2,結(jié)果:6765
執(zhí)行線程: pool-1-thread-4,結(jié)果:6765
執(zhí)行線程: pool-1-thread-6,結(jié)果:6765
執(zhí)行線程: pool-1-thread-8,結(jié)果:6765
執(zhí)行線程: pool-1-thread-5,結(jié)果:6765
執(zhí)行線程: pool-1-thread-3,結(jié)果:6765
執(zhí)行線程: pool-1-thread-7,結(jié)果:6765
執(zhí)行線程: pool-1-thread-10,結(jié)果:6765
執(zhí)行線程: pool-1-thread-9,結(jié)果:6765

從上述結(jié)果可以看出,為了保證吞吐量,該線程池為每個任務(wù)都創(chuàng)建了一個線程,當然這是在沒有線程空閑的情況下創(chuàng)建的新的線程。假設(shè)執(zhí)行前5個任務(wù)時都創(chuàng)建了一個線程,執(zhí)行到底6個任務(wù)時剛好前面的第一個任務(wù)執(zhí)行完畢,此時線程1空閑,那么第六個任務(wù)就會被執(zhí)行在第一個線程中,而不是重新創(chuàng)建。

執(zhí)行周期性任務(wù)的線程-ScheduledPoolExecutor

通過Executors的newScheduledThreadPool函數(shù)即可創(chuàng)建定時執(zhí)行任務(wù)的線程池。

private static void newScheduledThreadPool() throws InterruptedException,
        ExecutionException {

    ScheduledExecutorService service = Executors.newScheduledThreadPool(4);

    // 參數(shù)2為第一次延遲的時間,參數(shù)2為執(zhí)行周期
    service.scheduleAtFixedRate((new Runnable() {

        @Override
        public void run() {
            // TODO Auto-generated method stub
            System.out.println("執(zhí)行線程: " + Thread.currentThread().getName()
                    + ",定時計算 1結(jié)果:" + fibc(20));
        }
    }), 1, 2, TimeUnit.SECONDS);

    // 參數(shù)2為第一次延遲的時間,參數(shù)2為執(zhí)行周期
    service.scheduleAtFixedRate((new Runnable() {

        @Override
        public void run() {
            // TODO Auto-generated method stub
            System.out.println("執(zhí)行線程: " + Thread.currentThread().getName()
                    + ",定時計算2結(jié)果:" + fibc(30));
        }
    }), 1, 2, TimeUnit.SECONDS);

}

打印結(jié)果:

執(zhí)行線程: pool-1-thread-1,定時計算 1結(jié)果:6765
執(zhí)行線程: pool-1-thread-2,定時計算2結(jié)果:832040
執(zhí)行線程: pool-1-thread-1,定時計算 1結(jié)果:6765
執(zhí)行線程: pool-1-thread-3,定時計算2結(jié)果:832040
執(zhí)行線程: pool-1-thread-1,定時計算 1結(jié)果:6765
執(zhí)行線程: pool-1-thread-4,定時計算2結(jié)果:832040
執(zhí)行線程: pool-1-thread-1,定時計算 1結(jié)果:6765
執(zhí)行線程: pool-1-thread-3,定時計算2結(jié)果:832040
執(zhí)行線程: pool-1-thread-2,定時計算 1結(jié)果:6765
執(zhí)行線程: pool-1-thread-4,定時計算2結(jié)果:832040

該線程池有4個線程,我們指定了兩個定時任務(wù),因此該線程池中有兩個線程來定時執(zhí)行任務(wù),哪個線程空閑就調(diào)度哪個線程來執(zhí)行任務(wù)。

同步集合 程序中的優(yōu)化策略-CopyOnWrite

Copy-On-Write是一種用于程序設(shè)計中的優(yōu)化策略,其基本思路是,從多個線程共享同一個列表,當某個線程想要修改這個列表的元素時,會把列表中的元素復(fù)制一份,然后進行修改,修改完成之后再將新的元素設(shè)置給這個列表,這是一種延時懶惰策略。這樣做的好處是我們可以對CopyOnWrite容器進行并發(fā)的讀而不需要加鎖,因為當前容器不會添加、移除任何元素。所有CopyOnWrite容器也是一種讀寫分離的思想,讀和寫不同的容器。從JDK1.5起Java并發(fā)包提供了兩個使用CopyOnWrite機制實現(xiàn)的并發(fā)容器,它們是CopyOnWriteArrayList和CopyOnWriteSet。

通過這種寫時拷貝的原理可以將讀、寫分離,使并發(fā)場景下對列表的操作效率得到提高,但它的缺點是,在添加、移除元素時占用的內(nèi)存空間翻了一倍,因此,這是以空間換時間的策略。

提高并發(fā)效率-ConcurrentHasMap

HashTable使用synchronized來保證線程安全,但在線程競爭激烈的情況下HashTable的效率非常低下。因為當一個線程訪問HashTable同步方法時,其他線程訪問HashTable的同步方法時,可能會進入阻塞或輪詢狀態(tài)。如線程1使用put進行添加元素,線程2不但不能使用put方法添加元素,并且也不能使用個圖方法來獲取元素,所以競爭越激烈效率越低。

HashTable在競爭激烈的并發(fā)環(huán)境下表現(xiàn)出效率低下的原因是因為所有訪問HashTable的線程都必須競爭同一把鎖。
假如容器里有多把鎖,每一把鎖用于鎖容器其中一部分數(shù)據(jù),那么當多線程訪問容器里不同數(shù)據(jù)段的數(shù)據(jù)時,線程間就不會存在鎖競爭,從而可以有效的提高并發(fā)訪問效率,這就是ConcurrentHasMap所使用的鎖分段技術(shù),首先將數(shù)據(jù)分成一段一段的存儲,然后給每一段數(shù)據(jù)配一把鎖,當一個線程占用鎖訪問其中一個段數(shù)據(jù)的時候,其他段的數(shù)據(jù)也能被其他線程訪問。有些方法需要跨段,如size()和containsValue(),它們可能需要鎖定整個表而不僅是某個段,這需要按順序鎖定所有段,操作完畢后,又按順序釋放所有段的鎖。

有效的方法-BlockingQueue

BlockingQueue的重要方法:

函 數(shù) 名 作 用
add(e) 把元素e添加到隊列中,成功返回true,否則拋出異常
offer(e) 把元素e添加到隊列中,成功返回true,否則返回false
offer(e,time,unit) 把元素e添加到隊列中,成功返回true,否則在等待指定的時間之后繼續(xù)嘗試添加,如果失敗則返回false
put(e) 把元素e添加到隊列中,如果隊列不能容納,則調(diào)用此方法的線程被阻塞直到隊列里面有空間再繼續(xù)添加
take() 取出隊列中的首個元素,若隊列為空,則線程進入等待直到隊列中新的元素加入為止
poll(time,unit) 取出并移除隊列中的首個元素,如果在指定的時間內(nèi)沒有獲取元素,則返回null
element() 獲取隊首元素,如果隊列為null,那么拋出NoSuchElementException異常
peek() 獲取隊首元素,如果隊列為空,那么返回null
remove() 獲取并移除隊首元素,如果隊列為空,那么拋出NoSuchElementException異常

BlockingQueue常用的實現(xiàn)有:

ArrayBlockingQueue

LinkedBlockingQueue

LinkedBlockingDequeue

ConcurrentLinkedQueue

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/66131.html

相關(guān)文章

  • 線程編程完全指南

    摘要:在這個范圍廣大的并發(fā)技術(shù)領(lǐng)域當中多線程編程可以說是基礎(chǔ)和核心,大多數(shù)抽象并發(fā)問題的構(gòu)思與解決都是基于多線程模型來進行的。一般來說,多線程程序會面臨三類問題正確性問題效率問題死鎖問題。 多線程編程或者說范圍更大的并發(fā)編程是一種非常復(fù)雜且容易出錯的編程方式,但是我們?yōu)槭裁催€要冒著風險艱辛地學習各種多線程編程技術(shù)、解決各種并發(fā)問題呢? 因為并發(fā)是整個分布式集群的基礎(chǔ),通過分布式集群不僅可以大...

    mengera88 評論0 收藏0
  • Java并發(fā)編程筆記(一)

    摘要:并發(fā)編程實戰(zhàn)水平很高,然而并不是本好書。一是多線程的控制,二是并發(fā)同步的管理。最后,使用和來關(guān)閉線程池,停止其中的線程。當線程調(diào)用或等阻塞時,對這個線程調(diào)用會使線程醒來,并受到,且線程的中斷標記被設(shè)置。 《Java并發(fā)編程實戰(zhàn)》水平很高,然而并不是本好書。組織混亂、長篇大論、難以消化,中文翻譯也較死板。這里是一篇批評此書的帖子,很是貼切。俗話說:看到有這么多人罵你,我就放心了。 然而知...

    cnsworder 評論0 收藏0
  • 淺談Java并發(fā)編程系列(六) —— 線程的使用

    摘要:線程池的作用降低資源消耗。通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的資源浪費。而高位的部分,位表示線程池的狀態(tài)。當線程池中的線程數(shù)達到后,就會把到達的任務(wù)放到中去線程池的最大長度。默認情況下,只有當線程池中的線程數(shù)大于時,才起作用。 線程池的作用 降低資源消耗。通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的資源浪費。 提高響應(yīng)速度。當任務(wù)到達時,不需要等到線程創(chuàng)建就能立即執(zhí)行...

    Vicky 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<