并發(fā)與多線程開(kāi)發(fā)

認(rèn)識(shí)線程

? 線程的啟動(dòng)方式以及應(yīng)用場(chǎng)景
? 線程的狀態(tài)和常用方法
? 線程的優(yōu)先級(jí) 提升任務(wù)的響應(yīng)速度
? 線程間通訊 (子線程與主線程 發(fā)消息)

多線程開(kāi)發(fā)

? 線程安全 (關(guān)鍵字synchornized 鎖 , 原子類治专, 并發(fā)容器)
? 線程流程控制

線程池原理

? 任務(wù)調(diào)度
? 復(fù)用原理

多線程優(yōu)化

?線程池
?并發(fā)安全
?kotlin(協(xié)程)

線程與進(jìn)程
分類:ui線程 工作線程

5種線程創(chuàng)建方式

  • new Thread (2種創(chuàng)建方法)
    缺點(diǎn): 卻反統(tǒng)一管理寂恬,可能無(wú)限制新建線程吏祸,相互之間競(jìng)爭(zhēng)糟把,可能占用過(guò)多系統(tǒng)資源導(dǎo)致OOM 或者死機(jī).
public class ThreadDemo {
    public static void main(String[] args) {
        Thread t1 = new MyThread();
        
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("線程2 =" + i);
                }
            }
        });

        t1.start();
        t2.start();
        
    }

    static class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 10; i++) {
                System.out.println("線程1 = " + i);
            }
        }
    }
}
  • AsyncTask (3種寫(xiě)法)
    于是Android 提供了 AsyncTask, 輕量級(jí)任務(wù)工具類没咙,提供任務(wù)執(zhí)行的進(jìn)度回調(diào)到UI線程秦驯,
    場(chǎng)景:如果需要知道任務(wù)執(zhí)行的進(jìn)度掌动,多個(gè)任務(wù)串行
    缺點(diǎn):生命周期和宿主的生命周期不同不蝴乔,有可能會(huì)導(dǎo)致內(nèi)存泄漏记餐,默認(rèn)情況下所有任務(wù)都是串行執(zhí)行。
    解決方法 可以加上static不持有宿主的對(duì)象薇正,如果程序不能并發(fā)執(zhí)行片酝,就指定一個(gè)線程池.
public class AsyncTaskDemo  extends AsyncTask<String,Integer,String> {

    @Override
    protected String doInBackground(String... params) {
       //TODO 囚衔。。雕沿。练湿。。
        publishProgress(**);
        return params[0];
    }

    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(s);
        // 拿到結(jié)果  result
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        // 拿到進(jìn)度 审轮, values[0];
    }
    
}

main(){
        AsyncTaskDemo asyncTask = new AsyncTaskDemo();

        // #1, 默認(rèn)串行執(zhí)行肥哎,可以感知任務(wù)進(jìn)度
        asyncTask.execute( params );
        //  可以修改為并發(fā)執(zhí)行
        asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);


        // #2,如果不想知道任務(wù)執(zhí)行進(jìn)度,可以直接創(chuàng)建
        AsyncTask.execute(new Runnable() {
            @Override
            public void run() {
                // ......
            }
        });

        //# 3疾渣,直接創(chuàng)建 并發(fā)執(zhí)行
        AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
            @Override
            public void run() {

            }
        });
}
  • HandlerThread
    使用于主線程需要和工作線程通信篡诽,適用于持續(xù)性任務(wù),比如輪詢榴捡,所有任務(wù)串行執(zhí)行杈女。
    缺點(diǎn): 不會(huì)像普通線程一樣主動(dòng)銷毀資源,會(huì)一直運(yùn)行薄疚,所以可能會(huì)造成內(nèi)存泄漏碧信。
 private void handlerThread() {
        // 創(chuàng)建一個(gè)子線程
        HandlerThread thread = new HandlerThread("current-thread");
        thread.start();
        
        /**
         *  在主線程中新建一個(gè)Handler,持有 Thread 的 looper
     Looper.
         *  現(xiàn)在是主線程中的Handler 持有了一個(gè) 子線程的 Looper
         */
        MHandler handler = new MHandler(thread.getLooper());

        // handler 發(fā)送數(shù)據(jù) ,就是主線程 向子線程發(fā)送消息
        handler.sendEmptyMessage(1);


    }


    static class MHandler extends Handler {
        public MHandler(@NonNull Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            //  因?yàn)?Handler 持有了子線程的 Looper,所以此時(shí)實(shí)在子線程當(dāng)中街夭。
            Log.e("MHandler", "message = " + msg.what);
           
        }
    }


如果 
  MHandler handler = new MHandler();  // 沒(méi)有傳遞Looper
  handler.sendEmptyMessage(1);
  // 因?yàn)橹骶€程中創(chuàng)建 Handler 時(shí)會(huì)有個(gè)默認(rèn)的  Looper
  //這時(shí)捕獲消息的就是再主線程 中.
  • IntentService
    繼承與Service ,在創(chuàng)建IntentService時(shí),onCreate()內(nèi)部會(huì)創(chuàng)建一個(gè)子線程來(lái)完成耗時(shí)操作
    應(yīng)用場(chǎng)景: 適用于任務(wù)需要夸頁(yè)面讀取任務(wù)執(zhí)行的進(jìn)度躏筏、結(jié)果板丽。比如后臺(tái)上傳圖片,批量操作數(shù)據(jù)庫(kù)等趁尼。任務(wù)執(zhí)行完成后埃碱,就會(huì)自我結(jié)束,不需要手動(dòng) stopService()(與Service的區(qū)別)
public class MyIntentService extends IntentService {

    public MyIntentService() {
        super("MyIntentService");
    }


    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            String value = intent.getStringExtra("key");
            Log.e("MyIntentService", "傳遞 key = " + value);
        }
    }

}

main 中調(diào)用

       Intent intent = new Intent(this, MyIntentService.class);
        intent.putExtra("key", "value");
        startService(intent);

  • ThreadPoolExecutor
    使用與快速處理大量耗時(shí)較短的任務(wù)場(chǎng)景酥泞。
Executors.newCachedThreadPool();            // 線程可復(fù)用的線程池
Executors.newFixedThreadPool();             // 固定線程數(shù)量的線程池
Executors.newScheduledThreadPool();         // 可指定定時(shí)任務(wù)的線程池
Executors.newSingleThreadExecutor();        // 線程數(shù)量為1 的線程池.

線程的優(yōu)先級(jí)

一般而言砚殿,線程的優(yōu)先級(jí)越高,獲得Cpu片的概率越大芝囤。

       /**
         *  Jdk 提供的 似炎, [1,10] 數(shù)字越大,優(yōu)先級(jí)越高悯姊, UI線程的 優(yōu)先級(jí) 為 5 ;
         *
         */
        Thread thread = new Thread();
        thread.setPriority(1);


        /**
         * Android API 羡藐, 優(yōu)先級(jí)更精細(xì)的劃分為 [-20,19]悯许,數(shù)字越小仆嗦,優(yōu)先級(jí)越高, UI線程的 優(yōu)先級(jí)為 -10.
         * 效果較為明顯
         * 一般把耗時(shí)較長(zhǎng)的線程的優(yōu)先級(jí)設(shè)置較低級(jí)別,
         * 耗時(shí)較短較頻繁的線程的優(yōu)先級(jí)設(shè)置較高級(jí)別. 但是不要高于UI 線程.
         */
         android.os.Process.setThreadPriority();

線程的狀態(tài)及常用方法

image.png

wait()/notify()
? 當(dāng)前線程進(jìn)入等待狀態(tài)先壕,并且釋放資源對(duì)象鎖瘩扼,可使用notify(),notifyAll() 或者等待超時(shí)來(lái)喚醒.
使用與多線程同步谆甜,一個(gè)線程要等待另一個(gè)線程的結(jié)果或者部分結(jié)果。 注意 wait - notify 的執(zhí)行順序

 final Object object = new Object();
        class Runnable1 implements Runnable {
            @Override
            public void run() {
                Log.e("Runnable", "thread1 - start");

                synchronized (object) {
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Log.e("Runnable", "thread1 - end");
            }
        }

        class Runnable2 implements Runnable {

            @Override
            public void run() {
                Log.e("Runnable", "thread2 - start");
                
                synchronized (object) {
                    object.notify();
                }  
                
                /**
                 *  線程2 notify()喚醒了線程1 集绰,
                 *  如果線程2有耗時(shí)操作店印,就先執(zhí)行線程1 了.
                 */
          //      try {
          //          Thread.sleep(1000);
           //     } catch (InterruptedException e) {
           //         e.printStackTrace();
           //     }
               
                Log.e("Runnable", "thread2 - end");
            }
        }
        new Thread(new Runnable1()).start();
        new Thread(new Runnable2()).start();

以上, object就是對(duì)象鎖倒慧, 這里如果換成 this , 跟object有啥不同嗎按摘。 wait()的使用是要在 synchornized() 同步代碼塊里面執(zhí)行的.

理想狀態(tài)下,線程1 執(zhí)行了 wait()以后使當(dāng)前線程進(jìn)入等待狀態(tài)纫谅,于是線程2拿到Cpu片開(kāi)始執(zhí)行炫贤,然后通過(guò)notify()喚醒線程1。
但是實(shí)際情況付秕,線程1 和線程2 誰(shuí)先執(zhí)行可不知道兰珍,如果線程2先執(zhí)行了,輪到線程1執(zhí)行時(shí)询吴,到了wait() 就沒(méi)有喚醒了出現(xiàn)假死現(xiàn)象掠河。

為了規(guī)避這種情況,可以定義一個(gè) volatile boolean hasNotify = false, 線程2 notify() 以后 hsNotify = true 猛计; 在 線程1 wait()前判斷 if(!hasNotify){ wait() } 唠摹, 或者 wait(1000), 自動(dòng)喚醒,可以避免線程 一直 wait()奉瘤;

join()
? ? 在當(dāng)前線程中插入一條任務(wù)勾拉,當(dāng)任務(wù)完成以后線程才可以繼續(xù)執(zhí)行.

 class JoinThread extends Thread {
            @Override
            public void run() {
                super.run();
                Log.e("JoinThread", "run: 1 " + System.currentTimeMillis());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.e("JoinThread", "run:2 " + System.currentTimeMillis());
            }
        }

        JoinThread thread = new JoinThread();
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Log.e("JoinThread", "join: 3 " + System.currentTimeMillis());
}
執(zhí)行順訊 就是  1,2盗温,3
UI線程中插入了一個(gè) JoinThread藕赞,UI線程要等JoinThread執(zhí)行完畢以后才接著執(zhí)行。

yield()
?? 不咋的用
暫停當(dāng)前正在執(zhí)行的線程對(duì)象卖局,不會(huì)釋放資源鎖斧蜕。
sleep()
?? 使調(diào)用線程進(jìn)入休眠狀態(tài),但在一個(gè)synchornized塊中執(zhí)行sleep,線程雖然休眠砚偶,但不會(huì)釋放資源對(duì)象鎖.

 final Object object = new Object();
        class Runnable1 implements Runnable {
            @Override
            public void run() {
                Log.e("Runnable", "thread1 - start");

                synchronized (object) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Log.e("Runnable", "thread1 - end");
            }
        }

        class Runnable2 implements Runnable {
            @Override
            public void run() {
                synchronized (object) {
                    Log.e("Runnable", "thread2 - start");
                    Log.e("Runnable", "thread2 - end");
                }
            }
        }
        new Thread(new Runnable1()).start();
        new Thread(new Runnable2()).start();

線程1 和線程2 的執(zhí)行順序不保證批销,如果線程1先執(zhí)行,線程2會(huì)等線程1全部執(zhí)行完才執(zhí)行.

線程間通信

子線程發(fā)主線程發(fā)送消息
?? ?? post() 蟹演,runOnUiThread()...

主線程向子線程發(fā)送消息

 /**
         * 要讓主線程給子線程發(fā)送消息风钻, 必然要使子線程有處理消息的能力 ,Looper
         */
        class LooperThread extends Thread {
            Looper looper;

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

            // 但是由于 Thread 可能不會(huì)一 start 就執(zhí)行 run(),
            // 所以 looper可能為空
            // 于是  wait();
            public Looper getLooper() {
                if (looper == null && isAlive()) {
                    synchronized (this) {
                        try {
                            wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                return looper;
            }

            @Override
            public void run() {
                Looper.prepare();

                looper = Looper.myLooper();
                synchronized (this) {
                    notify();
                }
                // 開(kāi)啟Looper 的無(wú)線循環(huán);
                Looper.loop();
            }
        }


        LooperThread looperThread = new LooperThread("Looper - Thread");
        looperThread.start();

        // 創(chuàng)建的 Handler 持有了子線程的Looper, handler 就會(huì)捕獲有子線程中Looper分發(fā)的消息.
        Handler handler = new Handler(looperThread.getLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);

                Log.e("ThreadMessage", " message =  " + msg.what);
                Log.e("ThreadMessage", "thread - name: " + Thread.currentThread().getName());
            }
        };

        handler.sendEmptyMessage(2);

線程安全

(買票)
線程安全的本質(zhì):

? ? 能夠讓并發(fā)線程酒请,有序的運(yùn)行(有序可能是先來(lái)后到的排隊(duì)骡技,也可能是有人插隊(duì),但同一時(shí)刻只能有一個(gè)線程有權(quán)訪問(wèn)同步數(shù)據(jù)), 線程執(zhí)行的結(jié)果,能夠?qū)ζ渌€程可見(jiàn)布朦。

AutomicInteger 原子類

? ? 實(shí)現(xiàn)無(wú)鎖數(shù)據(jù)更新囤萤,自旋( do-while 循環(huán) ,線程不會(huì)阻塞) 的設(shè)計(jì)能夠有效避免線程阻塞-喚醒的系統(tǒng)資源開(kāi)銷是趴。 客戶端并發(fā)量不會(huì)很高.
適用于多線程技術(shù)涛舍,原子操作,并發(fā)量小的場(chǎng)景唆途。

volatile 可見(jiàn)性修飾

? ? volatile修飾的成員變量在每次被線程訪問(wèn)時(shí)富雅,都強(qiáng)迫從共享內(nèi)存重新讀取該成員的值,而且肛搬,當(dāng)成員變量值發(fā)生變化時(shí)没佑,強(qiáng)迫將變化的值重新寫(xiě)入共享內(nèi)存
不能解決非原子操作的線程安全性,性能不及原子類高温赔。

 volatile int count;
    private void increment() {

        // 其他線程可見(jiàn).
        count = 5;

        // 以下兩步操作都不是一步操作蛤奢,是非原子操作,所以不可被其他線程可見(jiàn).
        count = count + 1;
        count++;
    }

對(duì)比兩種方式

public class AtomicDemo {

    public static void main(String[] args) {

        final AtomicTask task = new AtomicTask();

        Runnable runnable = new Runnable() {
            @Override
            public void run() {

                for (int i = 0; i < 10000; i++) {
                    task.atoAdd();
                    task.volatileCount();
                }

            }
        };

        Thread t1 = new Thread(runnable);
        Thread t2 = new Thread(runnable);

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println("原子類操作的結(jié)果 = " + task.atomicInteger.get()); // 結(jié)果  20000
        System.out.println("volatile 操作的結(jié)果 = " + task.count);   // 結(jié)果 18720       
    }
    
    static class AtomicTask {
        AtomicInteger atomicInteger = new AtomicInteger();
        volatile int count = 0;
        public void atoAdd() {
            // 增量 陶贼,先 加1啤贩,然后返回操作之前的值.
            atomicInteger.getAndIncrement();

            // 減量 ,先獲取未更改前的值拜秧,然后 減1
            //atomicInteger.getAndDecrement();
        }
        public void volatileCount() {
            count++;
        }
    }
}

以上痹屹,在輸出結(jié)果中表現(xiàn) 使用volatile 修飾的變量 不保證線程安全。

synchornized

鎖java對(duì)象腹纳, 鎖Class類對(duì)象痢掠,鎖代碼塊.

  • 鎖方法 , 加在方法上,為獲取到對(duì)象鎖的其他線程不可以訪問(wèn)該方法
public class ThreadDemo {

    static ArrayList<String> tickets = new ArrayList<>();
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            tickets.add("票 -" + (i + 1));
        }
        saleTicket();
    }
    
    private static void saleTicket() {
        final SynchronizedDemo demo = new SynchronizedDemo();
        for (int i = 0; i < tickets.size(); i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    demo.buyTicket();
                }
            }).start();
        }
    }

    static class SynchronizedDemo {
        synchronized void buyTicket() {
            String name = Thread.currentThread().getName();
            System.out.println("買票人:" + name + " 已經(jīng)準(zhǔn)備好");

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("買票人 :" + name + " 買到的票是 " + tickets.remove(0));
        }

    }
}
==========  輸出結(jié)果 ============
買票人:Thread-0 已經(jīng)準(zhǔn)備好
買票人 :Thread-0 買到的票是 票 -1
買票人:Thread-4 已經(jīng)準(zhǔn)備好
買票人 :Thread-4 買到的票是 票 -2
買票人:Thread-3 已經(jīng)準(zhǔn)備好
買票人 :Thread-3 買到的票是 票 -3
買票人:Thread-2 已經(jīng)準(zhǔn)備好
買票人 :Thread-2 買到的票是 票 -4
買票人:Thread-1 已經(jīng)準(zhǔn)備好
買票人 :Thread-1 買到的票是 票 -5

以上, 針對(duì)同一個(gè) 買票人對(duì)象嘲恍,在butTicket()方法上加 synchronized就可以保證線程安全. 如果在多線程中訪問(wèn)不同對(duì)象的同步方法,就不能保證線程安全了.
因?yàn)榫€程同步是對(duì)同一個(gè)對(duì)象而言.
比如這樣, 每個(gè)線程中都創(chuàng)建一個(gè)買票人對(duì)象雄驹,

for (int i = 0; i < tickets.size(); i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    final SynchronizedDemo demo = new SynchronizedDemo();
                    demo.buyTicket();
                }
            }).start();
        }

==========  輸出結(jié)果 ============
買票人 :Thread-3 買到的票是 票 -2
買票人 :Thread-0 買到的票是 票 -1
買票人 :Thread-2 買到的票是 票 -4
買票人 :Thread-1 買到的票是 票 -3
買票人 :Thread-4 買到的票是 票 -1

=========  == 購(gòu)買的票出現(xiàn)了重復(fù) =====
  • 鎖Class 對(duì)象佃牛, 加載 static 方法上相當(dāng)于給Class對(duì)象加鎖,哪怕是不同的Java對(duì)象實(shí)例医舆,也需要排隊(duì)等候.
    如果 synchronized 加在 static方法上俘侠,線程安全
 static class SynchronizedDemo {
        synchronized static void buyTicket() {
            String name = Thread.currentThread().getName();
            System.out.println("買票人:" + name + " 已經(jīng)準(zhǔn)備好");

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("買票人 :" + name + " 買到的票是 " + tickets.remove(0));
        }
    }
=========== 輸出結(jié)果 ==========
買票人:Thread-0 已經(jīng)準(zhǔn)備好
買票人 :Thread-0 買到的票是 票 -1
買票人:Thread-4 已經(jīng)準(zhǔn)備好
買票人 :Thread-4 買到的票是 票 -2
買票人:Thread-3 已經(jīng)準(zhǔn)備好
買票人 :Thread-3 買到的票是 票 -3
買票人:Thread-2 已經(jīng)準(zhǔn)備好
買票人 :Thread-2 買到的票是 票 -4
買票人:Thread-1 已經(jīng)準(zhǔn)備好
買票人 :Thread-1 買到的票是 票 -5

以上,static加鎖蔬将,相當(dāng)于給Class類加鎖爷速, 在內(nèi)存中只有一個(gè),即使不同的買票人對(duì)象來(lái)訪問(wèn)霞怀,也是可以保證線程安全的.

  • 鎖代碼塊惫东,獲取到對(duì)象鎖的線程可以執(zhí)行同步代碼塊以外的代碼.
 void buyTicket() {
            String name = Thread.currentThread().getName();

            synchronized (this) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("買票人 :" + name + "正在買票");
            }
            System.out.println("買票人 :" + name + " 買到的票是 " + tickets.remove(0));
        }
================ 輸出結(jié)果 ===================
買票人 :Thread-1正在買票
買票人 :Thread-1 買到的票是 票 -1
買票人 :Thread-3正在買票
買票人 :Thread-3 買到的票是 票 -2
買票人 :Thread-4正在買票
買票人 :Thread-4 買到的票是 票 -3
買票人 :Thread-2正在買票
買票人 :Thread-2 買到的票是 票 -4
買票人 :Thread-0正在買票
買票人 :Thread-0 買到的票是 票 -5

以上 如果 synchronized (SynchronizedDemo.class){ .........買票...... }
能保證線程同步.

synchronized 優(yōu)劣勢(shì)
優(yōu)勢(shì):可以規(guī)避死鎖,即使同步代碼快中出現(xiàn)了異常,jvm也能自動(dòng)釋放鎖廉沮。
劣勢(shì): - 必須要獲取鎖對(duì)象的線程執(zhí)行完或者異常才可以釋放鎖颓遏,不能中斷
??? ??- 多個(gè)線程獲取鎖 是否成功也不知道,不夠靈活.
??? ??- 不能設(shè)置超時(shí)滞时。

ReentrantLock鎖

void lock() ;                 獲取不到會(huì)阻塞
boolean tryLock() ;           嘗試獲取鎖,獲取到鎖返回 true,

基本用法 :

public class ReentrantLockDemo {

    public static void main(String[] args) {
        final ReentrantTask task = new ReentrantTask();

        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {

                    task.buyTicket();
                }
            }).start();
        }
    }


    static class ReentrantTask {
        ReentrantLock lock = new ReentrantLock();

        void buyTicket() {
            String name = Thread.currentThread().getName();
            
            try {
                lock.lock();
                System.out.println(name + " :準(zhǔn)備買票");
                Thread.sleep(100);
                System.out.println(name + " : 票買好了");

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}
=============== 輸出結(jié)果 ============
Thread-0 :準(zhǔn)備買票
Thread-0 : 票買好了
Thread-1 :準(zhǔn)備買票
Thread-1 : 票買好了
Thread-2 :準(zhǔn)備買票
Thread-2 : 票買好了
Thread-3 :準(zhǔn)備買票
Thread-3 : 票買好了
Thread-4 :準(zhǔn)備買票
Thread-4 : 票買好了
Thread-5 :準(zhǔn)備買票
Thread-5 : 票買好了

可重入叁幢,避免死鎖
在不釋放鎖的情況下,重復(fù)獲取鎖的對(duì)象.

            try {
                lock.lock();
                System.out.println(name + " :準(zhǔn)備買票");
                Thread.sleep(100);
                System.out.println(name + " : 票買好了");


                lock.lock();
                System.out.println(name + " :準(zhǔn)備買票");
                Thread.sleep(100);
                System.out.println(name + " : 票買好了");

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
                lock.unlock();
            }

==============  輸出結(jié)果 ==============
Thread-0 :準(zhǔn)備買票
Thread-0 : 票買好了
Thread-0 : 又準(zhǔn)備買票
Thread-0 : 票又買好了
Thread-1 :準(zhǔn)備買票
Thread-1 : 票買好了
Thread-1 : 又準(zhǔn)備買票
Thread-1 : 票又買好了
Thread-2 :準(zhǔn)備買票
Thread-2 : 票買好了
Thread-2 : 又準(zhǔn)備買票
Thread-2 : 票又買好了
Thread-3 :準(zhǔn)備買票
Thread-3 : 票買好了
Thread-3 : 又準(zhǔn)備買票
Thread-3 : 票又買好了
Thread-4 :準(zhǔn)備買票
Thread-4 : 票買好了
Thread-4 : 又準(zhǔn)備買票
Thread-4 : 票又買好了

公平鎖與非公平鎖
公平鎖 : 所有進(jìn)入阻塞隊(duì)列的線程依次有機(jī)會(huì)執(zhí)行.
(默認(rèn))非公平鎖:允許線程插隊(duì)坪稽,避免每一個(gè)線程都阻塞再被喚醒曼玩,性能較高。但是因?yàn)榭梢圆尻?duì)的原因窒百,會(huì)導(dǎo)致有的線程一直得不到執(zhí)行.

ReentrantLock lock = new ReentrantLock( true / false );

簡(jiǎn)單模擬多線程打印功能

public class ReentrantDemo2 {

    public static void main(String[] args) {

        final PrintTask task = new PrintTask();

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

            new Thread(new Runnable() {
                @Override
                public void run() {
                    task.print();
                }
            }).start();
        }
        
    }


    static class PrintTask {
        private ReentrantLock lock = new ReentrantLock(true);

        void print() {
            String name = Thread.currentThread().getName();
            try {
                lock.lock();
                System.out.println(name + ": 第一次打印");
                Thread.sleep(1000);
                lock.unlock();


                lock.lock();
                System.out.println(name + ": 第二次打印");
                Thread.sleep(1000);

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
    
}

如果 公平鎖
輸出結(jié)果

Thread-0: 第一次打印
Thread-1: 第一次打印
Thread-2: 第一次打印
Thread-3: 第一次打印
Thread-4: 第一次打印
Thread-0: 第二次打印
Thread-1: 第二次打印
Thread-2: 第二次打印
Thread-3: 第二次打印
Thread-4: 第二次打印

如果非公平鎖

Thread-0: 第一次打印
Thread-0: 第二次打印
Thread-1: 第一次打印
Thread-1: 第二次打印
Thread-2: 第一次打印
Thread-2: 第二次打印
Thread-3: 第一次打印
Thread-3: 第二次打印
Thread-4: 第一次打印
Thread-4: 第二次打印

以上黍判,表明公平鎖釋放鎖以后,每個(gè)線程獲取到鎖的機(jī)會(huì)是均等的贝咙, 如果是非公平鎖样悟,當(dāng)?shù)谝淮未蛴⊥赆尫沛i以后,因?yàn)槠渌€程的狀態(tài)都是阻塞的庭猩,重新喚醒需要有一定的資源消耗窟她,而當(dāng)前線程釋放掉以后還沒(méi)有進(jìn)入阻塞狀態(tài),由當(dāng)前線程來(lái)獲取鎖對(duì)象是開(kāi)銷最小的蔼水。

應(yīng)用場(chǎng)景:
公平鎖:交易
非公平鎖 :synchornized, 場(chǎng)景就很多了.

ReentrantLock- Condintion對(duì)象

使用 await - singnal 可以指定喚醒一個(gè)(或一組)線程震糖。

共享鎖 & 排他鎖 (ReentrantReadWriteLock)

共享鎖,所有線程都可同時(shí)獲得趴腋,并發(fā)量高吊说,比如在線查看文檔
排他鎖,同一時(shí)刻只能有一個(gè)線程有權(quán)修改資源优炬,比如在線文檔編輯.

??如何正確的使用鎖和原子類

一般情況下 synchronized 已經(jīng)足夠使用颁井,
如果需要知道鎖的細(xì)節(jié),就要使用Lock

?? 調(diào)優(yōu)

  • 減少持鎖的時(shí)間蠢护,比如synchronized 同步代碼快雅宾,非持有鎖的線程可以執(zhí)行同步代碼塊之外的程序,可以減少線程等待時(shí)間.
  • 鎖粗化 葵硕, 如果多次同步代碼塊之間有一部分耗時(shí)很短不用同步的代碼眉抬,可以將這些程序合并為一次同步.
  • 在并發(fā)量不大的情況下,可以考慮使用原子類懈凹。

線程池

??為什么引入線程池

  • 降低資源消耗 : 通過(guò)重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀的造成的消耗蜀变。
  • 提高響應(yīng)速度: 當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不用等待線程的創(chuàng)建就可以立即執(zhí)行介评。
  • 提高線程的可管理性:線程是稀缺資源库北,如果無(wú)限制的創(chuàng)建,不僅會(huì)造成系統(tǒng)資源的浪費(fèi),還會(huì)影響系統(tǒng)的穩(wěn)定性贤惯,使用線程池統(tǒng)一分配洼专,調(diào)優(yōu)和監(jiān)控.

??Java中默認(rèn)的線程池

??線程池實(shí)現(xiàn)原理

??線程池中線程復(fù)用的原理

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市孵构,隨后出現(xiàn)的幾起案子屁商,更是在濱河造成了極大的恐慌,老刑警劉巖颈墅,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜡镶,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡恤筛,警方通過(guò)查閱死者的電腦和手機(jī)官还,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)毒坛,“玉大人望伦,你說(shuō)我怎么就攤上這事〖逡螅” “怎么了屯伞?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)豪直。 經(jīng)常有香客問(wèn)我劣摇,道長(zhǎng),這世上最難降的妖魔是什么弓乙? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任末融,我火速辦了婚禮,結(jié)果婚禮上暇韧,老公的妹妹穿的比我還像新娘勾习。我一直安慰自己,他們只是感情好懈玻,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布语卤。 她就那樣靜靜地躺著,像睡著了一般酪刀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上钮孵,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天骂倘,我揣著相機(jī)與錄音,去河邊找鬼巴席。 笑死历涝,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播荧库,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼堰塌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了分衫?” 一聲冷哼從身側(cè)響起场刑,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蚪战,沒(méi)想到半個(gè)月后牵现,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡邀桑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年瞎疼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片壁畸。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贼急,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出捏萍,到底是詐尸還是另有隱情太抓,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布照弥,位于F島的核電站腻异,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏这揣。R本人自食惡果不足惜悔常,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望给赞。 院中可真熱鬧机打,春花似錦、人聲如沸片迅。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)柑蛇。三九已至芥挣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間耻台,已是汗流浹背空免。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盆耽,地道東北人蹋砚。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓扼菠,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親坝咐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子循榆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355