成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

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

wums / 490人閱讀

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

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

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

對(duì)于Java的多線程來說,我們學(xué)習(xí)的一般都是Thread和Runnable,通過我們使用如下代碼啟動(dòng)一個(gè)新的線程:

private void startewThread() {

    new Thread(){

        @Override
        public void run() {

            // 耗時(shí)任務(wù)
        }

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

    new Thread(new Runnable() {

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

        }
    }).start();
}

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

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

Thread和Runnable的關(guān)系

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

public class Thread implements Runnable {

    //要執(zhí)行的目標(biāo)任務(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則獲取當(dāng)前線程的線程組
               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ù)啟動(dòng)線程
            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();
        }
    }
}

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

public interface Runnable {

    public void run();

}

當(dāng)啟動(dòng)一個(gè)線程時(shí),如果Thread的target不為空,則會(huì)在子線程中執(zhí)行這個(gè)target的run方法,否則虛擬機(jī)就會(huì)執(zhí)行該線程自身的run方法。

線程的wait、sleep、join和yield

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

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

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

public class WaitDemo {

    private static Object lockObject = new Object();

    private static void waitAndNotifAll() {

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

        //創(chuàng)建并啟動(dò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ù)--->等待耗時(shí): " + 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();

    }

}

運(yùn)行結(jié)果:

主線程運(yùn)行
主線程等待
...
...

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

wait、notify機(jī)制通常用于等待機(jī)制的實(shí)現(xiàn),當(dāng)條件未滿足時(shí)調(diào)用wait進(jìn)入等待狀態(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。意思就是阻塞當(dāng)前調(diào)用join函數(shù)的任務(wù)所在的線程,直到該任務(wù)執(zhí)行完成后再繼續(xù)執(zhí)行所在線程的任務(wù)。下面我們來看看一個(gè)具體是實(shí)例:

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("啟動(dòng)線程1--執(zhí)行完畢");

        try {
            //等待worker1任務(wù)執(zhí)行完成
            worker1.join();
            
            System.out.println("啟動(dòng)線程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í)行
啟動(dòng)線程1--執(zhí)行完畢
Work in worker-1
啟動(dòng)線程2--執(zhí)行完畢
Work in worker-2
主線程繼續(xù)執(zhí)行
主線程執(zhí)行完畢

上述代碼的邏輯是主線程開始執(zhí)行、啟動(dòng)線程1、等待線程1執(zhí)行完畢、啟動(dòng)線程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í)行時(shí)間給其他已就緒狀態(tài)的線程。

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

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)先級(jí)為: " + this.getPriority()+ "--->" + i);
                
                //當(dāng)i為2時(shí) 調(diào)用當(dāng)前線程yield函數(shù)
                if (i== 2) {
                    Thread.yield();
                }
            }
        }
    }
}

打印結(jié)果:

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

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

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

Callable、Future和FutureTask

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

Callable

Callable與Runnable的功能大致相似不同的是Callable是一個(gè)泛型接口,它有一個(gè)泛型參數(shù)V,該接口中有一個(gè)返回值(類型為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為線程池制定了一個(gè)可管理的任務(wù)標(biāo)準(zhǔn)。它提供了對(duì)Runnable或者Callable任務(wù)的執(zhí)行結(jié)果進(jìn)行取消、查詢是否完成、獲取結(jié)果、設(shè)置結(jié)果操作,分別對(duì)應(yīng)cancel、isDone、get、set函數(shù)。get方法會(huì)阻塞,直到任務(wù)返回結(jié)果。Future的聲明如下:

public interface Future {

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

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

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

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

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

Future只是定義了一些規(guī)范的接口,而FutureTask則是它的實(shí)現(xiàn)類。FutureTask實(shí)現(xiàn)了RunnableFuture,而RunnableFuture實(shí)現(xiàn)了Runnable又實(shí)現(xiàn)了Future這兩個(gè)接口,因此FutureTask同時(shí)具備他們的功能。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那樣對(duì)Runnable和Callable進(jìn)行包裝,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則會(huì)被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實(shí)現(xiàn)了Runnable,因此它既可以通過Thread包裝來執(zhí)行,也可以提交給ExecuteService來執(zhí)行,并且還可以通過get()函數(shù)來獲取執(zhí)行結(jié)果,該函數(shù)會(huì)阻塞,直到結(jié)果返回。因此,F(xiàn)utureTask既是Future、Runnable,又是包裝了Callable(Runnable最終也會(huì)被轉(zhuǎn)換為Callable),它是這兩者的合體。

下面示例演示Runnable、Callable、FutureTask的運(yùn)用,代碼如下:

public class FutureTaskDemo {

    //線程池
    static ExecutorService mExecutor = Executors.newSingleThreadExecutor();
    
    /**
     * 向線程池提交Runnable對(duì)象
     */
    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對(duì)象
     * @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對(duì)象
     * @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
線程池

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

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

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

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

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

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

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

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

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

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

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

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

下面對(duì)參數(shù)進(jìn)行詳細(xì)說明:

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

ArrayBlockingQueue

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

LinkedBlockingQueue

基于鏈表結(jié)構(gòu)的無界隊(duì)列,此隊(duì)列按FIFO原則對(duì)任務(wù)進(jìn)行排序。因?yàn)樗菬o界的,所以才有此隊(duì)列后線程池將忽略handler參數(shù)。

SynchronousQueue

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

PriorityBlockingQueue

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

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

AbortPolicy

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

CallerRunsPolicy

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

DiscardOldestPolicy

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

DiscardPolicy

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

newFixedThreadPool

對(duì)應(yīng)Android平臺(tái)來說,最常使用的就是通過Executors.newFixedThreadPool(int size)函數(shù)來啟動(dòng)固定數(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+"次計(jì)算結(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次計(jì)算結(jié)果: 6765
執(zhí)行線程: pool-1-thread-2
第1次計(jì)算結(jié)果: 6765
執(zhí)行線程: pool-1-thread-3
第2次計(jì)算結(jié)果: 6765
執(zhí)行線程: pool-1-thread-1
第3次計(jì)算結(jié)果: 6765
執(zhí)行線程: pool-1-thread-2
第4次計(jì)算結(jié)果: 6765
執(zhí)行線程: pool-1-thread-3
第5次計(jì)算結(jié)果: 6765
執(zhí)行線程: pool-1-thread-1
第6次計(jì)算結(jié)果: 6765
執(zhí)行線程: pool-1-thread-2
第7次計(jì)算結(jié)果: 6765
執(zhí)行線程: pool-1-thread-3
第8次計(jì)算結(jié)果: 6765
執(zhí)行線程: pool-1-thread-1
第9次計(jì)算結(jié)果: 6765

在上述例子中,我們啟動(dòng)了含有3個(gè)線程的線程池,調(diào)用的是Executors的newFixedThreadPool函數(shù),該函數(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ù)隊(duì)列,這樣該線程池中就含有固定個(gè)數(shù)的線程,并且能夠容納無數(shù)個(gè)任務(wù)。

newCacheThreadPool

有時(shí)可能需要任務(wù)盡可能快地被執(zhí)行,這就需要線程池中的線程足夠多也就是說此時(shí)需要拿空間來換時(shí)間,線程越多占用的內(nèi)存消耗就越大。因此,我們可能需要一種場(chǎng)景,如果來了一個(gè)新的任務(wù),并且沒有空閑線程可用,此時(shí)必須馬上創(chuàng)建一個(gè)線程來立即執(zhí)行任務(wù)。我們可以通過Executors的newCacheThreadPool函數(shù)來實(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é)果可以看出,為了保證吞吐量,該線程池為每個(gè)任務(wù)都創(chuàng)建了一個(gè)線程,當(dāng)然這是在沒有線程空閑的情況下創(chuàng)建的新的線程。假設(shè)執(zhí)行前5個(gè)任務(wù)時(shí)都創(chuàng)建了一個(gè)線程,執(zhí)行到底6個(gè)任務(wù)時(shí)剛好前面的第一個(gè)任務(wù)執(zhí)行完畢,此時(shí)線程1空閑,那么第六個(gè)任務(wù)就會(huì)被執(zhí)行在第一個(gè)線程中,而不是重新創(chuàng)建。

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

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

private static void newScheduledThreadPool() throws InterruptedException,
        ExecutionException {

    ScheduledExecutorService service = Executors.newScheduledThreadPool(4);

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

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

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

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

}

打印結(jié)果:

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

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

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

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

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

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

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

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

有效的方法-BlockingQueue

BlockingQueue的重要方法:

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

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

ArrayBlockingQueue

LinkedBlockingQueue

LinkedBlockingDequeue

ConcurrentLinkedQueue

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/66131.html

相關(guān)文章

  • 線程編程完全指南

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

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

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

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

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

    Vicky 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<