javaweb學習總結(四十七)——監聽器(Listener)在開發中的應用
轉自:http://www.cnblogs.com/xdp-gacl/p/3965508.htmlhtml
監聽器在JavaWeb開發中用得比較多,下面說一下監聽器(Listener)在開發中的常見應用前端
1、統計當前在線人數
在JavaWeb應用開發中,有時候咱們須要統計當前在線的用戶數,此時就可使用監聽器技術來實現這個功能了。java
1 package me.gacl.web.listener; 2 3 import javax.servlet.ServletContext; 4 import javax.servlet.http.HttpSessionEvent; 5 import javax.servlet.http.HttpSessionListener; 6 7 /** 8 * @ClassName: OnLineCountListener 9 * @Description: 統計當前在線用戶個數 10 * @author: 孤傲蒼狼 11 * @date: 2014-9-10 下午10:01:26 12 * 13 */ 14 public class OnLineCountListener implements HttpSessionListener { 15 16 @Override 17 public void sessionCreated(HttpSessionEvent se) { 18 ServletContext context = se.getSession().getServletContext(); 19 Integer onLineCount = (Integer) context.getAttribute("onLineCount"); 20 if(onLineCount==null){ 21 context.setAttribute("onLineCount", 1); 22 }else{ 23 onLineCount++; 24 context.setAttribute("onLineCount", onLineCount); 25 } 26 } 27 28 @Override 29 public void sessionDestroyed(HttpSessionEvent se) { 30 ServletContext context = se.getSession().getServletContext(); 31 Integer onLineCount = (Integer) context.getAttribute("onLineCount"); 32 if(onLineCount==null){ 33 context.setAttribute("onLineCount", 1); 34 }else{ 35 onLineCount--; 36 context.setAttribute("onLineCount", onLineCount); 37 } 38 } 39 }
2、自定義Session掃描器
當一個Web應用建立的Session不少時,爲了不Session佔用太多的內存,咱們能夠選擇手動將這些內存中的session銷燬,那麼此時也能夠藉助監聽器技術來實現。git
1 package me.gacl.web.listener; 2 3 import java.util.Collections; 4 import java.util.LinkedList; 5 import java.util.List; 6 import java.util.ListIterator; 7 import java.util.Timer; 8 import java.util.TimerTask; 9 import javax.servlet.ServletContextEvent; 10 import javax.servlet.ServletContextListener; 11 import javax.servlet.http.HttpSession; 12 import javax.servlet.http.HttpSessionEvent; 13 import javax.servlet.http.HttpSessionListener; 14 15 /** 16 * @ClassName: SessionScanerListener 17 * @Description: 自定義session掃描器 18 * @author: 孤傲蒼狼 19 * @date: 2014-9-10 下午10:16:42 20 * 21 */ 22 public class SessionScanerListener implements HttpSessionListener,ServletContextListener { 23 24 /** 25 * @Field: list 26 * 定義一個集合存儲服務器建立的HttpSession 27 * LinkedList不是一個線程安全的集合 28 */ 29 /** 30 * private List<HttpSession> list = new LinkedList<HttpSession>(); 31 * 這樣寫涉及到線程安全問題,SessionScanerListener對象在內存中只有一個 32 * sessionCreated可能會被多我的同時調用, 33 * 當有多我的併發訪問站點時,服務器同時爲這些併發訪問的人建立session 34 * 那麼sessionCreated方法在某一時刻內會被幾個線程同時調用,幾個線程併發調用sessionCreated方法 35 * sessionCreated方法的內部處理是往一個集合中添加建立好的session,那麼在加session的時候就會 36 * 涉及到幾個Session同時搶奪集合中一個位置的狀況,因此往集合中添加session時,必定要保證集合是線程安全的才行 37 * 如何把一個集合作成線程安全的集合呢? 38 * 可使用使用 Collections.synchronizedList(List<T> list)方法將不是線程安全的list集合包裝線程安全的list集合 39 */ 40 //使用 Collections.synchronizedList(List<T> list)方法將LinkedList包裝成一個線程安全的集合 41 private List<HttpSession> list = Collections.synchronizedList(new LinkedList<HttpSession>()); 42 //定義一個對象,讓這個對象充當一把鎖,用這把鎖來保證往list集合添加的新的session和遍歷list集合中的session這兩個操做達到同步 43 private Object lock = new Object(); 44 45 @Override 46 public void sessionCreated(HttpSessionEvent se) { 47 System.out.println("session被建立了!!"); 48 HttpSession session = se.getSession(); 49 50 synchronized (lock){ 51 /** 52 *將該操做加鎖進行鎖定,當有一個thread-1(線程1)在調用這段代碼時,會先拿到lock這把鎖,而後往集合中添加session, 53 *在添加session的這個過程當中假設有另一個thread-2(線程2)來訪問了,thread-2多是執行定時器任務的, 54 *當thread-2要調用run方法遍歷list集合中的session時,結果發現遍歷list集合中的session的那段代碼被鎖住了, 55 *而這把鎖正在被往集合中添加session的那個thread-1佔用着,所以thread-2只能等待thread-1操做完成以後纔可以進行操做 56 *當thread-1添加完session以後,就把lock放開了,此時thread-2拿到lock,就能夠執行遍歷list集合中的session的那段代碼了 57 *經過這把鎖就保證了往集合中添加session和變量集合中的session這兩步操做不能同時進行,必須按照先來後到的順序來進行。 58 */ 59 list.add(session); 60 } 61 } 62 63 @Override 64 public void sessionDestroyed(HttpSessionEvent se) { 65 System.out.println("session被銷燬了了!!"); 66 } 67 68 /* Web應用啓動時觸發這個事件 69 * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent) 70 */ 71 @Override 72 public void contextInitialized(ServletContextEvent sce) { 73 System.out.println("web應用初始化"); 74 //建立定時器 75 Timer timer = new Timer(); 76 //每隔30秒就定時執行任務 77 timer.schedule(new MyTask(list,lock), 0, 1000*30); 78 } 79 80 @Override 81 public void contextDestroyed(ServletContextEvent sce) { 82 System.out.println("web應用關閉"); 83 } 84 } 85 86 /** 87 * @ClassName: MyTask 88 * @Description:定時器要定時執行的任務 89 * @author: 孤傲蒼狼 90 * @date: 2014-9-11 上午12:02:36 91 * 92 */ 93 class MyTask extends TimerTask { 94 95 //存儲HttpSession的list集合 96 private List<HttpSession> list; 97 //存儲傳遞過來的鎖 98 private Object lock; 99 public MyTask(List<HttpSession> list,Object lock){ 100 this.list = list; 101 this.lock = lock; 102 } 103 /* run方法指明瞭任務要作的事情 104 * @see java.util.TimerTask#run() 105 */ 106 @Override 107 public void run() { 108 //將該操做加鎖進行鎖定 109 synchronized (lock) { 110 System.out.println("定時器執行!!"); 111 ListIterator<HttpSession> it = list.listIterator(); 112 /** 113 * 迭代list集合中的session,在迭代list集合中的session的過程當中可能有別的用戶來訪問, 114 * 用戶一訪問,服務器就會爲該用戶建立一個session,此時就會調用sessionCreated往list集合中添加新的session, 115 * 然而定時器在定時執行掃描遍歷list集合中的session時是沒法知道正在遍歷的list集合又添加的新的session進來了, 116 * 這樣就致使了往list集合添加的新的session和遍歷list集合中的session這兩個操做沒法達到同步 117 * 那麼解決的辦法就是把"list.add(session)和while(it.hasNext()){//迭代list集合}"這兩段代碼作成同步, 118 * 保證當有一個線程在訪問"list.add(session)"這段代碼時,另外一個線程就不能訪問"while(it.hasNext()){//迭代list集合}"這段代碼 119 * 爲了可以將這兩段不相干的代碼作成同步,只能定義一把鎖(Object lock),而後給這兩步操做加上同一把鎖, 120 * 用這把鎖來保證往list集合添加的新的session和遍歷list集合中的session這兩個操做達到同步 121 * 當在執行往list集合添加的新的session操做時,就必須等添加完成以後纔可以對list集合進行迭代操做, 122 * 當在執行對list集合進行迭代操做時,那麼必須等到迭代操做結束以後纔可以每每list集合添加的新的session 123 */ 124 while(it.hasNext()){ 125 HttpSession session = (HttpSession) it.next(); 126 /** 127 * 若是當前時間-session的最後訪問時間>1000*15(15秒) 128 * session.getLastAccessedTime()獲取session的最後訪問時間 129 */ 130 if(System.currentTimeMillis()-session.getLastAccessedTime()>1000*30){ 131 //手動銷燬session 132 session.invalidate(); 133 //移除集合中已經被銷燬的session 134 it.remove(); 135 } 136 } 137 } 138 } 139 }
以上就是監聽器的兩個簡單應用場景。web
分類:
JavaWeb學習總結
標籤:
JavaWeb學習總結
@Override
public
void
sessionDestroyed(HttpSessionEvent se) {
ServletContext context = se.getSession().getServletContext();
Integer onLineCount = (Integer) context.getAttribute(
"onLineCount"
);
if
(onLineCount==
null
){
context.setAttribute(
"onLineCount"
,
1
);
}
else
{
onLineCount--;
context.setAttribute(
"onLineCount"
, onLineCount);
}
}
統計在線人數的代碼裏面:
銷燬的Session域裏面的用戶時,這裏的代碼有這樣一段,
if(onLineCount == null)
{
context.setAttribute("onLineCount",1);
}
超出個人理解範圍了。
(並且這個代碼,單線程還好,共享變量沒有進行同步處理,可能要出問題)
(好吧、後面就加鎖了)
後面一段代碼中:
if(System.currentTimeMillis()-session.getLastAccessedTime()>1000*30){
//手動銷燬session
session.invalidate();
//移除集合中已經被銷燬的session
it.remove();
}
以爲能夠調換一下順序:
//移除集合中已經被銷燬的session
it.remove();
//手動銷燬session
session.invalidate();