轉載:javaweb學習總結(四十七)——監聽器(Listener)在開發中的應用

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

好文要頂 已關注 收藏該文
5
0
 
« 上一篇: WebService學習總結(一)——WebService的相關概念
» 下一篇: JavaWeb學習總結(四十八)——模擬Servlet3.0使用註解的方式配置Servlet

posted on 2014-11-16 12:35 孤傲蒼狼 閱讀(25090) 評論(5) 編輯 收藏安全

評論

#1樓 2015-01-09 09:38 友人M  

 

     
 
1
2
3
4
5
6
7
8
9
10
11
@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();
 
     

  回覆引用服務器

#2樓 2015-06-03 13:52 山川盡美  

 

     
 

@ 友人M
以爲能夠調換一下順序:
//移除集合中已經被銷燬的session
it.remove();
//手動銷燬session
session.invalidate();

這裏不能夠調換微信

 
     

  回覆引用session

#3樓 2015-12-18 14:59 weidi  

 

     
 

online是個單詞,是否是應該換成Online併發

 
     

  回覆引用

#4樓 2016-12-03 00:57 PengWenHao  

 

     
 

怎麼只有後臺代碼,沒有前端代碼呢?

 
     

  回覆引用

#5樓 2016-12-19 10:14 輕吻指尖  

 

     
 

有個問題沒想明白:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void sessionCreated(HttpSessionEvent se) {
         System.out.println( "session被建立了!!" );
         HttpSession session = se.getSession();
         
         synchronized (lock){
             /**
              *將該操做加鎖進行鎖定,當有一個thread-1(線程1)在調用這段代碼時,會先拿到lock這把鎖,而後往集合中添加session,
              *在添加session的這個過程當中假設有另一個thread-2(線程2)來訪問了,thread-2多是執行定時器任務的,
              *當thread-2要調用run方法遍歷list集合中的session時,結果發現遍歷list集合中的session的那段代碼被鎖住了,
              *而這把鎖正在被往集合中添加session的那個thread-1佔用着,所以thread-2只能等待thread-1操做完成以後纔可以進行操做
              *當thread-1添加完session以後,就把lock放開了,此時thread-2拿到lock,就能夠執行遍歷list集合中的session的那段代碼了
              *經過這把鎖就保證了往集合中添加session和變量集合中的session這兩步操做不能同時進行,必須按照先來後到的順序來進行。
              */
             list.add(session);
         }
向集合中添加session的操做已經加鎖了,那麼集合自己還須要線程安全嗎?
相關文章
相關標籤/搜索