八、Java高級特性(wait,notify/notifyAll)等待超時模式實現一個連接池

一、前言

今天我們通過wait,notify/notifyAll的知識實現一個等待超時的數據庫連接池

二、代碼

1、自定義一個數據庫連接類

package com.it.test.thread;

public class MyConnction {
    public void createStatement() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public void commit() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

2、創建連接池

package com.it.test.thread;

import java.util.LinkedList;

public class ConnectionPool {
    /**
     * 數據庫連接池
     */
    LinkedList<MyConnction> pools = new LinkedList<>();
    /**
     * 默認1S的超時時間
     */
    private static final int TIME = 100;


    /**
     * 初始化連接池的
     *
     * @param size
     */
    public ConnectionPool(int size) {
        for (int i = 0; i < size; i++) {
            pools.addLast(new MyConnction());
        }
    }

    /**
     * 從連接池中獲取連接
     *
     * @return
     */
    public MyConnction getPool() {
        synchronized (pools) {
            long outTime = System.currentTimeMillis() + TIME;
            /**
             * 如果連接是空的,並且超時時間未結束,則等待休眠
             */
            while (pools.isEmpty() && (outTime - System.currentTimeMillis()) > 0) {
                try {
                    //每次被喚醒之後(有可能被其他線程喚醒)
                    //根據計算剩餘的超時時間繼續休眠
                    pools.wait(outTime - System.currentTimeMillis());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if (!pools.isEmpty()) {
                return pools.removeFirst();
            } else {
                return null;
            }


        }


    }

    /**
     * 釋放連接
     */
    public void releasePool(MyConnction connection) {
        if (connection != null) {
            synchronized (pools) {
                pools.addLast(connection);
                pools.notifyAll();

            }

        }


    }


}

3、編寫測試類

package com.it.test.thread.pool;


import com.it.test.thread.ConnectionPool;
import com.it.test.thread.MyConnction;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

/**
 *類說明:
 */
public class DBPoolTest {
    static ConnectionPool pool  = new ConnectionPool(10);
    // 控制器:控制main線程將會等待所有Woker結束後才能繼續執行
    static CountDownLatch end;

    public static void main(String[] args) throws Exception {
        // 線程數量
        int threadCount = 50;
        end = new CountDownLatch(threadCount);
        int count = 20;//每個線程的操作次數
        AtomicInteger got = new AtomicInteger();//計數器:統計可以拿到連接的線程
        AtomicInteger notGot = new AtomicInteger();//計數器:統計沒有拿到連接的線程
        for (int i = 0; i < threadCount; i++) {
            Thread thread = new Thread(new Worker(count, got, notGot), 
                    "worker_"+i);
            thread.start();
        }
        end.await();// main線程在此處等待
        System.out.println("總共嘗試了: " + (threadCount * count));
        System.out.println("拿到連接的次數:  " + got);
        System.out.println("沒能連接的次數: " + notGot);
    }

    static class Worker implements Runnable {
        int           count;
        AtomicInteger got;
        AtomicInteger notGot;

        public Worker(int count, AtomicInteger got,
                               AtomicInteger notGot) {
            this.count = count;
            this.got = got;
            this.notGot = notGot;
        }

        public void run() {
            while (count > 0) {
                try {
                    // 從線程池中獲取連接,如果1000ms內無法獲取到,將會返回null
                    // 分別統計連接獲取的數量got和未獲取到的數量notGot
                    MyConnction connection = pool.getPool();
                    if (connection != null)
                    {
                        try {
                            connection.createStatement();
//                            PreparedStatement preparedStatement
//                                    = connection.prepareStatement("");
//                            preparedStatement.execute();
                            connection.commit();
                        } finally {
                            pool.releasePool(connection);
                            got.incrementAndGet();
                        }
                    } else {
                        notGot.incrementAndGet();
                        System.out.println(Thread.currentThread().getName()
                                +"等待超時!");
                    }
                } catch (Exception ex) {
                } finally {
                    count--;
                }
            }
            end.countDown();
        }
    }
}

總共嘗試了: 1000
拿到連接的次數:  652
沒能連接的次數: 348

總結

wait,notify/notifyAll屬於Object下的方法,使用的時候必須在同步代碼塊內,也就是必須在synchronized 關鍵字修飾的代碼內。

  • 調用wait方法的時候,會釋放當前的鎖,進入等待狀態
  • 調用notify和notifyAll方法,會隨機喚醒或者全部喚醒一個線程,儘量使用notifyAll。因爲notifyAll喚醒的是全部線程。當之前進入wait狀態的線程收到notify喚醒通知之後,重新競爭鎖,如果競爭到了,那麼就在調用wait方法的地方繼續往下執行。
    -調用wait方法會釋放當前鎖,而調用notify和notifyAll不會釋放當前鎖,只有執行完notify部分的代碼完,纔會釋放鎖,所以通常我們把調用notify方法方在最後。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章