最近在學習PSP,其核心思想是:記錄自己的
工作數(shù)據(jù),通過數(shù)據(jù)找出有問題的地方予以改進,通過數(shù)據(jù)預測自己將來執(zhí)行某項任務時所需要的時間。
如果衡量開發(fā)人員的工作需要真實地記錄他們工作的執(zhí)行情況的話,那么開發(fā)人員似乎沒有很大的動力做這件事,因為:
他們似乎需要花費大量的時間執(zhí)行一些和任務本身沒有關系的事情。
由于PSP也需要開發(fā)人員記錄自己代碼的缺陷,因此開發(fā)人員可能寧可不記錄,來讓自己顯得“不那么笨”。
所以,我必須找到一種激勵方法,讓他們愿意如實地記錄自己代碼中注入的缺陷。我們可能不能采用“發(fā)現(xiàn)缺陷則懲罰”的方法,因為代碼中總是有缺陷的。我們或許可以采取一定的獎勵措施,這種獎勵措施由“短期的、相對容易實現(xiàn)的目標”和“長期的、不容易實現(xiàn)的目標”組成。前者的目的是引導開發(fā)人員經(jīng)常性地關注自己的代碼質量,努力降低缺陷率。由于是短期的且相對容易實現(xiàn)的,則相應的獎勵也比較小。而如果在一個較長的時間內開發(fā)人員能夠始終保持低缺陷率,則第二種獎勵便可自動達到。
例如,“短期的、相對容易實現(xiàn)的目標”可以是在一個迭代中“每千行代碼包含的缺陷數(shù)量低于10個”;“長期的、不容易實現(xiàn)的目標”可以是在連續(xù)的12個迭代中至多只有2個迭代的缺陷率沒有達到“每千行代碼包含的缺陷數(shù)量低于10個”。
我們不應該采取“發(fā)現(xiàn)缺陷則獎勵”的措施,因為這會激勵
測試人員去匯報一大堆無關緊要的缺陷。對于測試人員,可以采用“產(chǎn)品發(fā)布后,在一定時間內客戶沒有報出一定數(shù)量的缺陷,則獎勵測試人員。”
無論是對于開發(fā)人員還是測試人員,這種獎勵最好是針對團隊整體的,或者至少是團隊層面和個人層面都有的,而不要僅僅在個人層面。這樣做的期望是讓每個人都為團隊整體的績效負責,同時在某些人可能明顯拖整體后腿的前提下,讓一些一直努力的人可以得到獎勵。
要讓開發(fā)人員明白,他們的職責是兩點:
按時開發(fā)出符合質量要求的產(chǎn)品。
為公司省錢。事實上,這第二點要求是第一點要求的連帶產(chǎn)品:只要“按時”和“符合質量”,就為公司省下了錢。
最終的目標:讓團隊在保證工作質量的前提下,過上朝九晚五的
生活。注意反之是不成立的。
English » | | | | | | | | |
Text-to-speech function is limited to 100 characters
在書場上看到很多有關Java的書籍,但這就像進了瓜地里挑瓜挑的眼花,很多人不知道自己到底該選那本書好。很快精通Java可能只有很少一部分人能實現(xiàn),那就是他曾經(jīng)精通過 哪門語言,因為程序設計語言很好學,只要你精通一門語言,就可以做到一通百通。因為每種語言都有其共同點,就拿C語言來說,由于C語言出現(xiàn)的比較早,用的人也比較多,所以人們都習慣了它的語法規(guī)則和設計流程,假如現(xiàn)在出現(xiàn)了一門新的語言,而它和C語言的語法規(guī)則是天壤之隔,那么它的結果肯定是被淘汰的對 象。道理很簡單,這種新語言的語法習慣和人們的編程習慣相差甚遠,所以導致很少有人用,而語言的開發(fā)就是為了更多的使用才有其價值,如果沒人使用也就沒有它的價值了。就像Java語言一樣,它的出現(xiàn)要比C語言晚,但無論它再怎么新,它的語法規(guī)則和C語言基本相差不遠,所以人們也喜歡用,這樣它才能實現(xiàn)它的實際價值。就像筆者在學習JavaScript一樣,由于對Java的
學習比較深入,所以在學習JavaScript只需要不到一個星期就做出了像hao123那樣的網(wǎng)頁。
而對于大多人來說,他們如果沒有精通某種語言,剛開始就學Java,這樣連基本的語法規(guī)則都沒有積累,怎么可能在短期內精通Java?而本書就克服了這個缺點,無論是對于初學者還是大牛,都有其相對應的適應性。根據(jù)筆者自學Java兩年的經(jīng)驗,筆者在這里毛遂自薦一下,其實精通一門語言很簡單:對于初學者,剛開始需要把基本概念過一遍,而本書開始部分的基本概念都是精簡版,所以這樣就克服了概念吸收慢的缺點。接著就是做后面的程序練習和項目開發(fā)。有人可能會問,這樣如果有的概念忘記了怎么辦?很正常,遇到不懂的概念就回去前面查或者查API文檔。就這么簡單,精通的過程就是在不斷地查和練之間形成的;對于已經(jīng)接觸過一門語言的同學前面的Java概念只需簡單過一遍,畢竟每種語言之間雖然有很多相似之處,但也有很多不同之處,所以主要看不同的地方。接著還 是不斷地練習和做項目,這樣才能不斷提高自己。
我在這里不得不提一下另一種古老的學習方式,那就是中學的學習方式。很多人將中學的學習方式帶到了大學,而大學的學習方式和中學的學習方式是大相徑庭的,無論你學習什么。所以就出現(xiàn)了,很多在中學學習很少拔尖的同學在大學的學習中卻很吃力,甚至付出了很多努力,但最后的成績還是到不得自己預期的水平。在中 學的學習方式是我們花大量的時間來把概念夠透徹,尤其對于數(shù)學更是這樣,就拿筆者來說,筆者在高考時把五本數(shù)學書仔仔細細翻了三遍,課后習題一個不落的往后做。而在大學,大學就是一個小社會,它會讓你更接近現(xiàn)實,同時進入社會事情肯定也越來越多,怎樣高效地處理這些事情就需要另一種學習方式。就像筆者在上面說過的一樣,在大學的學習中大多是靠自己自學的,在大學靠老師就等于靠一面快要倒了的墻,你是靠不住的,這樣只會耽誤一個學生的前途。所以,我們在學習過程中,怎么高效地吸收書本上的知識,很簡單,就是通過不斷地查和練。
以前在中學時,經(jīng)??匆恍┰鯓犹岣邔W習效率、怎樣考高分的書,感覺人家說得在情在理,自己當時也看得是激情澎湃。但在大學的圖書館鉆了兩年后筆者才發(fā)現(xiàn)看 不看這些其實都是一樣的。因為無論在哪本學習方法的書里面,都是讓你把自己的時間安排的滿滿的來學習一門知識,這很明顯是理想狀態(tài),進入社會的人有多少能整體學習一門知識的,就是學生每天也要學習不同的課程,更何況進入社會的我們。其實,話又說回來了,別人的學習方法也不無道理,人各有志,每個人的情況大 相徑庭。但無論你無論是借鑒別人的學習方法,還是自己的,只有適合自己的才是最好的。
還有一點,學習方法固然重要,但更重要的是自己的心態(tài),如果一個三天打魚兩天曬網(wǎng),那么,無論多么科學的學習方法對他來說都無濟于事。道理很簡單,就像一個人對他的女朋友用心不專一樣,那么他還希望他的女朋友能和他相處一輩子嗎?
對于初學者來說,筆者建議剛開始練習Java程序的時候用DOS環(huán)境來編譯和運行,這樣也可以提高自己的程序調試水平。筆者承認Eclipse功能很強大 用起來也非常方便,但筆者認為這不適合初學者使用,因為里面很多函數(shù)、類、方法等不需要自己寫就可以自動生成,這樣反而不利于初學者的學習。這個道理也很簡單,其實,越方便的東西我們越要警惕,這就和天上掉餡餅是一個道理,它有可能不是圈套就是陷阱。
剛開始學習Java不在多,關鍵在精。很多人在學習時有這樣一種誤區(qū),書借了很多,但是都是這本書學一點,那本書學一點,到頭來學的知識沒有一個整體性,最后給自己的感覺就是好像學了很多,但真正用起來卻手足無措。所以,你只需要用一本書把它搞精就OK了。
在這里我們需要明確一個誤區(qū):Java的學習是為了項目開發(fā),而不是為了搞研究。所以,我們在學習的時候關鍵是要知道它怎樣用,而不是要深入地知道它到底 是怎么回事。而筆者只所以要寫這本書,這也是其中一個原因。緣由筆者在剛開始學習Java的時候也借了很多書,但都是理論搞得過于深刻,這樣不但繁瑣難懂,而且最后用起來還是寫不出來。就拿里面的IO流那章來說,很多書都想把它講的很清楚這點沒錯,所以理論搞得非常深厚,但這樣只會讓人看得一頭霧水而不知所云。這樣反而會事倍功半,所以筆者在講這章時,很簡單,主要是搞清楚流的去向,如讀出就是把文件從內存讀出到顯示器,寫入就是通過鍵盤把文件寫入到內存。搞清楚了最基本的道理,后面的各個函數(shù)都是圍繞這一點來展開的,學起來就輕而易舉地理解它。這就和練功一樣,先要把內功練深厚,后面的深奧功夫才能很快練就,反之,就只會走火入魔。
English » | | | | | | | | |
Text-to-speech function is limited to 100 characters
電腦上的許多軟件可以監(jiān)控瀏覽器發(fā)出的HTTP請求, iPhone上有許多連網(wǎng)程序但沒有自帶軟件可以實現(xiàn)監(jiān)控, 為了方便
測試這些請求是否正確而省去在程序中記錄請求日志并逐一查找的麻煩, 可以利用Paros這個監(jiān)控軟件來實現(xiàn). 以下是實現(xiàn)在Mac上監(jiān)控iPhone發(fā)出的HTTP請求的具體步驟:
1. 將Mac機器的以太網(wǎng)共享給無線局域網(wǎng):
Mac上: 系統(tǒng)偏好設置->共享->Internet 共享, 將"以太網(wǎng)"共享給"AirPort"
2. 查看無線局域網(wǎng)IP:
返回->網(wǎng)絡->AirPort, 查看到AirPort具有IP地址"169.254.242.107"
3. 設置Paros本地代理:
Paros->Tools->Options->Local proxy, Address填上"169.254.242.107", 端口默認為8080
4. 設置iPhone上網(wǎng)代理:
iPhone上: 設置->無線局域網(wǎng)->選上Mac機器共享出去的網(wǎng)絡->按"Details"箭頭->Http代理->手動->服務器填"169.254.242.107", 端口填"8080"
5. 查看HTTP請求:
iPhone打開訪問任何連網(wǎng)程序, 可以在Paros的Sites下看到訪問的網(wǎng)站, 右邊可以選Reques/Response等信息.
English » | | | | | | | | |
Text-to-speech function is limited to 100 characters
目前公司研發(fā)使用
jira軟件進行
項目管理,安裝了GreenHopper,JIRA Subversion plugin,Links Hierarchy Reports等插件。jira數(shù)據(jù)庫采用oracle 11g。由于歷史原因,采用的
操作系統(tǒng)版本為windows
server 2008 32位。
一:新需求匯總:
1: 把jira遷移到windows server 2008 64位的新服務器上
2:新安裝fisheye插件并解決授權問題
3: jira,fisheye,svn的整合
4:調整jira的運行內存
二:操作步驟及注意事項
1:新服務器上安裝jira軟件,安裝路徑務必同舊服務器保持一致,否則會出現(xiàn)插件缺失的情況!要采用和舊服務器同樣的jira軟件版本,這里采用jira版本4.4.5
2:下載并安裝64位jdk,這里采用
java版本1.6.0_43
C:\Users\Administrator>java -version
java version "1.6.0_43"
Java(TM) SE Runtime Environment (build 1.6.0_43-b01)
Java HotSpot(TM) 64-Bit Server VM (build 20.14-b01, mixed mode)
3:下載并安裝fisheye,這里采用fisheye-2.4.3版本,下載完成后解壓運行bin目錄下的start.bat即可啟動。
4:fisheye授權問題,參考
百度文庫鏈接即可!
5:jira同fisheye以及svn的整合
進入jira的管理員頁面,點擊源代碼控制,點擊fisheye configuration標簽進行整合
English » | | | | | | | | |
Text-to-speech function is limited to 100 characters
微軟對測試人員有提供以下training roadmap 0. Day1-2: Cross Discipline New Employee Orientation
1. 0-2 years
Test Automation
Debugging
Model Based Testing
Elective Courses
2. 2-5 years
Technical Electives: Design patterns,
SQL server, C#, C++, protocols, other skill based courses
3. 5-10 years: 才算是senior tester
目前和公司的訓練課程比較,微軟在0-2 years做的事情和我們差不多, 不過我們是缺少了debugging這一塊, 也能是這一塊并不容易處理, 或者是目前我們這方面的人才也不夠, 所以排不出這樣的課程.
不過到了2-5years, 微軟在這方面做的就不錯. 目前我們公司在這方面就沒有太多著墨, 或許我們在deveopler有開這方面的課, 但是我們卻沒有強迫tester去參加. 如果我們沒有加強這一塊, tester會不容易在前期有更多幫助, 或者是在review, debugging能出更多力. 所以這應該是之后我們公司要努力的一塊.
另外, 微軟也要求測試人員, 除了hard skill外, 也需要具備以下的soft skill
1. 分析解決問題的能力
2. 客戶為主的創(chuàng)新
3. 卓越技術
5. 對品質的熱情
6. 戰(zhàn)略眼光: 可以幫助我們超越競爭者和增加stakeholder的價值
7. 自信: 要有自信知道這些bug是對顧客非常重要, 因此一但發(fā)生這些問題時, 便會強力要求開發(fā)人員修改
8. 沖擊和影響: 影響力是來自于自信和經(jīng)驗,沖擊是來自知道如何讓改變發(fā)生
9. 跨界合作: 創(chuàng)新常常來自于跨部門或組織的合作, 若是只注意自己的功能或測試這樣不會成功
10. 自我意識: 會自我撿討自我批判, 來不段
學習和改進
English » | | | | | | | | |
Text-to-speech function is limited to 100 characters
前言:
在做WEB項目時,我們寫好了一個Dao和Service后,接下來就是要進行
單元測試,測試的時候還要等到Spring容器全部加載完畢后才能進行,然后通過拿到ApplicationContext對象來gerBean()方法進行測試,或者更笨點的就是寫一個控制器,在瀏覽器敲入地址進行deBug跟蹤測試,這樣不僅效率低,而且收效甚微。
本章來講解spring融合Junit4進行單元測試。
本章的測試源目錄和包是緊隨上一章節(jié)的源代碼。點我查看上一章節(jié)
jar包支持(上一章節(jié)代碼里面已給出)
測試的源代碼和包結構(同上)
注意:測試類
test包路徑最好位于src根目錄下,編譯后為calsses文件夾下,方便其他路徑的書寫
實例代碼演示:
****************復制該類至上一章節(jié)test包下即可************注釋部分我盡可能詳細講解****************
UserServiceTest package test; import javax.annotation.Resource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort.Direction; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; import com.spring.jpa.user.User; import com.spring.jpa.user.UserService; /** 聲明用的是Spring的測試類 **/ @RunWith(SpringJUnit4ClassRunner.class) /** 聲明spring主配置文件位置,注意:以當前測試類的位置為基準,有多個配置文件以字符數(shù)組聲明 **/ @ContextConfiguration(locations={"../spring-config/spring-jpa.xml"}) /** 聲明使用事務,不聲明spring會使用默認事務管理 **/ @Transactional /** 聲明事務回滾,要不測試一個方法數(shù)據(jù)就沒有了豈不很杯具,注意:插入數(shù)據(jù)時可注掉,不讓事務回滾 **/ @TransactionConfiguration(transactionManager="transactionManager",defaultRollback=true) public class UserServiceTest { @Resource private UserService userService; @Test // 新增(來個20條數(shù)據(jù)) 注意新增的時候先把事務注掉,要不會回滾操作 public void testSaveUser() { for(int i=0; i<20; i++){ User user = new User(); user.setUserName("system"); user.setPassWord(i+"system"); userService.saveUser(user); } } @Test // 刪除 有事務回滾,并不會真的刪除 public void testDeleteUser() { userService.deleteUser(27L); } @Test // 查詢所有 public void testFindAllUser() { List<User> users = userService.findAllUsers(); System.out.println(users.size()); } @Test // 查詢分頁對象 public void testFindAllUserByPage() { /** * 創(chuàng)建一個分頁對象 (注意:0代表的是第一頁,5代表每頁的大小,后兩個參數(shù)不寫即為默認排序) * Direction:為一個枚舉類,定義了DESC和ASC排序順序 * id:結果集根據(jù)id來進行DESC降序排序 * 想自己實現(xiàn)的話,最好繼承他這個類,來定義一些個性的方法 */ PageRequest request = new PageRequest(1, 4, Direction.DESC, "id"); Page<User> users = userService.findAllUserByPage(request); // 打印分頁詳情 System.out.println("查詢結果:共"+users.getTotalElements()+"條數(shù)據(jù),每頁顯示"+users.getSize()+"條,共"+users.getTotalPages()+"頁,當前第"+(users.getNumber()+1)+"頁!"); // 打印結果集的內容 System.out.println(users.getContent()); } // main 用于查看spring所有bean,以此可以檢測spring容器是否正確初始化 public static void main(String[] args) { // 我這里使用的是絕對路徑,請根據(jù)你項目的路徑來配置(相對路徑挖不出來-OUT了) String [] path = {"E:/moviework/springJpa/src/spring-config/spring-jpa.xml"}; ApplicationContext ac = new FileSystemXmlApplicationContext(path); String[] beans = ac.getBeanDefinitionNames(); for(String s : beans) { System.out.println(s); // 打印bean的name } } } |
測試testFindAllUserByPage()方法控制臺輸出sql語句和信息:
完事,就是這么簡單,和普通java類的測試多的只是注解的東西。原理還是一樣的,并且它支持事務的回滾,不用擔心在測試的時候對數(shù)據(jù)進行破壞。只有用了你才能體會原來Spring 框架的 WEB項目測試也可以這么的簡潔。
數(shù)據(jù)都是基于上一章節(jié)來的,本章節(jié)不再貼出,項目打包的下載地址也在上一章節(jié)。點我前往上一章節(jié)
總結:
平時在編寫test類的時候,寫在src目錄下更方便閱讀和代碼的編寫
遵守測試規(guī)范,測試類方法名為:test + 原方法名首字母大寫
注意@ContextConfiguration注解路徑的引用
English » | | | | | | | | |
Text-to-speech function is limited to 100 characters
本文仿照
Windows 回收站的功能,運用 Bash 腳本在
Linux 上做了實現(xiàn),創(chuàng)建 delete 腳本代替 rm 命令對文件或目錄進行刪除操做。該腳本實現(xiàn)了以下功能:對大于 2G 的文件或目錄直接刪除,否則放入$HOME/trash 目錄下;恢復 trash 目錄中的被刪除文件到原目錄下;文件存放在 trash 目錄中超過七天被自動刪除。
概述
刪除是危險系數(shù)很高的操作,一旦誤刪可能會造成難以估計的損失。在 Linux 系統(tǒng)中這種危險尤為明顯,一條簡單的語句:rm –rf /* 就會把整個系統(tǒng)全部刪除,而 Linux 并不會因為這條語句的不合理而拒絕執(zhí)行。 在 Windows 中,為了防止誤刪,系統(tǒng)提供了回收站功能。用戶在執(zhí)行刪除操作后,文件并不會直接從硬盤中刪除,而是被放到回收站中。在清空回收站前,如果發(fā)現(xiàn)有文件被誤刪,用戶可以將回收站中的文件恢復到原來的位置。而 Linux 并沒有提供類似功能,刪除命令 rm 一旦確認執(zhí)行,文件就會直接從系統(tǒng)中刪除,很難恢復。
回收站構成
本文共用三個腳本實現(xiàn)了回收站的主要功能:Delete 腳本、logTrashDir 腳本和 restoreTrash 腳本。其中 Delete 腳本是核心腳本,其作用是重新封裝 rm 命令。相對于 rm 的直接刪除,該命令會先將文件或目錄
移動到$home/trash 目錄下。如果用戶想要將文件直接刪除,可以用 -f 選項,delete 腳本會直接調用 rm –f 命令將文件從硬盤上刪除。logTrashDir 腳本用于將被刪除文件的信息記錄到 trash 目錄下的一個隱藏文件中。restoreTrash 腳本用來將放入 trash 中的文件或目錄重新恢復到原路徑下。在 Linux 系統(tǒng)中,只要將這三個腳本放到/bin/目錄下,并用 chmod +X filename 賦予可執(zhí)行權限,即可直接使用。下面將介紹每個腳本的主要部分
Delete 腳本
創(chuàng)建目錄
首先要創(chuàng)建目錄來存放被刪除的文件,本文在用戶根目錄$HOME 下建立 trash 目錄來存放文件。具體代碼如下:
清單 1.創(chuàng)建回收站目錄
realrm="/bin/rm"
if [ ! -d ~/trash ]
then
mkdir -v ~/trash
chmod 777 ~/trash
fi
如上所示,先判斷目錄是否已建立,如未建立,即第一次運行該腳本,則創(chuàng)建 trash 目錄。變量 realrm 存放了 Linux 的 rm 腳本位置,用于在特定條件下調用以直接刪除文件或目錄。
輸出幫助信息
該腳本在用戶僅輸入腳本名而未輸入?yún)?shù)執(zhí)行時,輸出簡要幫助信息,代碼如下:
清單 2.輸出幫助信息
if [ $# -eq 0 ]
then
echo "Usage:delete file1 [file2 file3....]"
echo "If the options contain -f,then the script will exec 'rm' directly"
如代碼所示,該腳本的運用格式是 delete 后跟要刪除的文件或目錄的路徑,中間用空格隔開。
直接刪除文件
有些用戶確認失效并想直接刪除的文件,不應放入回收站中,而應直接從硬盤中刪除。Delete 腳本提供了-f 選項來執(zhí)行這項操作:
清單 3.直接刪除文件
while getopts "dfiPRrvW" opt do case $opt in f) exec $realrm "$@" ;; *) # do nothing ;; esac done |
如果用戶在命令中加入了-f 選項,則 delete 腳本會直接調用 rm 命令將文件或目錄直接刪除。如代碼中所示,所有的參數(shù)包括選項都會傳遞給 rm 命令。所以只要選項中包括選項-f 就等于調用 rm 命令,可以使用 rm 的所有功能。如:delete –rfv filename 等于 rm –rfv filename。
用戶交互
需要與用戶確認是否將文件放入回收站。相當于 Windows 的彈窗提示,防止用戶誤操作。
清單 4.用戶交互
echo -ne "Are you sure you want to move the files to the trash?[Y/N]:\a"
read reply
if [ $reply = "y" -o $reply = "Y" ]
then #####
判斷文件類型并直接刪除大于 2G 文件
本腳本只對普通文件和目錄做操作,其他類型文件不做處理。先對每個參數(shù)做循環(huán),判斷他們的類型,對于符合的類型再判斷他們的大小是否超過 2G,如果是則直接從系統(tǒng)中刪除,避免回收站占用太大的硬盤空間。
清單 5.刪除大于 2G 的文件
for file in $@ do if [ -f "$file" –o –d "$file" ] then if [ -f "$file" ] && [ `ls –l $file|awk '{print $5}'` -gt 2147483648 ] then echo "$file size is larger than 2G,will be deleted directly" `rm –rf $file` elif [ -d "$file" ] && [ `du –sb $file|awk '{print $1}'` -gt 2147483648 ] then echo "The directory:$file is larger than 2G,will be deleted directly" `rm –rf $file` |
如以上代碼所示,該腳本用不同的命令分別判斷目錄和文件的大小。鑒于目錄的大小應該是包含其中的文件以及子目錄的總大小,所以運用了’du -sb’命令。兩種情況都使用了 awk 來獲取特定輸出字段的值來作比較。
移動文件到回收站并做記錄
該部分是 Delete 腳本的主要部分,主要完成以下幾個功能
獲取參數(shù)的文件名。因為用戶指定的參數(shù)中可能包含路徑,所以要從中獲取到文件名,用來生成 mv 操作的參數(shù)。該腳本中運用了字符串正則表達式’${file##*/}’來獲取。
生成新文件名。在原文件名中加上日期時間后綴以生成新的文件名,這樣用戶在瀏覽回收站時,對于每個文件的刪除日期即可一目了然。
生成被刪文件的絕對路徑。為了以后可能對被刪文件進行的恢復操作,要從相對路徑生成絕對路徑并記錄。用戶輸入的參數(shù)可能有三種情況:只包含文件名的相對路徑,包含點號的相對路徑以及絕對路徑,腳本中用字符串處理對三種情況進行判斷,并進行相應的處理。
調用 logTrashDir 腳本,將回收站中的新文件名、原文件名、刪除時間、原文件絕對路徑記錄到隱藏文件中
將文件通過 mv 命令移動到 Trash 目錄下。詳細代碼如下所示:
清單 6.移動文件到回收站并做記錄
now=`date +%Y%m%d_%H_%M_%S` filename="${file##*/}" newfilename="${file##*/}_${now}" mark1="." mark2="/" if [ "$file" = ${file/$mark2} ] then fullpath="$(pwd)/$file" elif [ "$file" != ${file/$mark1} ] then fullpath="$(pwd)${file/$mark1}" else fullpath="$file" fi echo "the full path of this file is :$fullpath" if mv -f $file ~/trash/$newfilename then $(/logTrashDir "$newfilename $filename $now $fullpath") echo "files: $file is deleted" else echo "the operation is failed" fi |
logTrashDir 腳本
該腳本較簡單,僅是一個簡單的文件寫入操作,之所以單獨作為一個腳本,是為了以后擴展的方便,具體代碼如下:
清單 7.logTrashDir 代碼
if [ ! -f ~/trash/.log ]
then
touch ~/trash/.log
chmod 700~/trash/.log
fi
echo $1 $2 $3 $4>> ~/trash/.log
該腳本先建立.log 隱藏文件,然后往里添加刪除文件的記錄。
restoreTrash 腳本
該腳本主要完成以下功能:
從.log 文件中找到用戶想要恢復的文件對應的記錄。此處依然使用 awk,通過正表達式匹配找到包含被刪除文件名的一行
從記錄中找到記錄原文件名的字段,以給用戶提示
將回收站中的文件移動到原來的位置,在這里運用了 mv –b 移動文件,之所以加入-b 選項是為了防止原位置有同名文件的情況。
將.log 文件中與被恢復文件相對應的記錄刪除
清單 8.獲取相應記錄
originalPath=$(awk /$filename/'{print $4}' "$HOME/trash/.log")
清單 9.查找原文件名及現(xiàn)文件名字段
filenameNow=$(awk /$filename/'{print $1}' ~/trash/.log)
filenamebefore=$(awk /$filename/'{print $2}' ~/trash/.log)
echo "you are about to restore $filenameNow,original name is $filenamebefore"
echo "original path is $originalPath"
清單 10.恢復文件到原來位置并刪除相應記錄
echo "Are you sure to do that?[Y/N]" read reply if [ $reply = "y" ] || [ $reply = "Y" ] then $(mv -b "$HOME/trash/$filename" "$originalPath") $(sed -i /$filename/'d' "$HOME/trash/.log") else echo "no files restored" fi |
自動定期清理 trash 目錄
因為 delete 操作并不是真正刪除文件,而是移動操作,經(jīng)過一段時間的積累,trash 目錄可能會占用大量的硬盤空間,造成資源浪費,所以定期自動清理 trash 目錄下的文件是必須得。本文的清理規(guī)則是:在回收站中存在 7 天以上的文件及目錄將會被自動從硬盤中刪除。運用的工具是 Linux 自帶的 crontab。
Crontab 是 Linux 用來定期執(zhí)行程序的命令。當安裝完成操作系統(tǒng)之后,默認便會啟動此任務調度命令。Crontab 命令會定期檢查是否有要執(zhí)行的工作,如果有要執(zhí)行的工作便會自動執(zhí)行該工作。而 Linux 任務調度的工作主要分為以下兩類:
1、系統(tǒng)執(zhí)行的工作:系統(tǒng)周期性所要執(zhí)行的工作,如備份系統(tǒng)數(shù)據(jù)、清理緩存
2、個人執(zhí)行的工作:某個用戶定期要做的工作,例如每隔 10 分鐘檢查郵件服務器是否有新信,這些工作可由每個用戶自行設置。
首先編寫 crontab 執(zhí)行時要調用的腳本 cleanTrashCan.如清單 10 所示,該腳本主要完成兩項功能:
判斷回收站中的文件存放時間是否已超過 7 天,如果超過則從回收站中刪除。
將刪除文件在.log 文件中相應的記錄刪除,保持其中數(shù)據(jù)的有效性,提高查找效率。
清單 11.刪除存在回收站超過 7 天的文件并刪除.log 中相應記錄
arrayA=($(find ~/trash/* -mtime +7 | awk '{print $1}')) for file in ${arrayA[@]} do $(rm -rf "${file}") filename="${file##*/}" echo $filename $(sed -i /$filename/'d' "$HOME/trash/.log") done |
腳本編寫完成后通過 chmod 命令賦予其執(zhí)行權限,然后運過 crontab –e 命令添加一條新的任務調度:
10 18 * * * /bin/ cleanTrashCan
該語句的含義為,在每天的下午 6 點 10 分執(zhí)行 cleanTrashCan 腳本
通過這條任務調度,trash 的大小會得到有效的控制,不會持續(xù)增大以致影響用戶的正常操作。
實際應用
首先要將 delete 腳本,logTrashDir 腳本,restoreTrash 腳本和 cleanTrashCan 放到/bin 目錄下,然后用 chmod +x delete restoreTrash logTrashDir cleanTrashCan 命令賦予這三個腳本可執(zhí)行權限。
運用 delete 腳本刪除文件,例如要刪除在/usr 目錄下的 useless 文件。根據(jù)用戶目前所在的位置,可以用相對路徑或絕對路徑來指定參數(shù),如:delete useless,delete ./useless 或者 delete /usr/useless。執(zhí)行過程如圖 1 所示:
圖 1.delete 腳本執(zhí)行過程
執(zhí)行之后,useless 文件會從原目錄中刪除,被移動到$HOME/trash 下,并被重命名,如圖 2.所示:
圖 2.回收站目錄
生成的.log 記錄如圖 3.所示:
圖 3.log 記錄
如果用戶在七天之內發(fā)現(xiàn)該文件還有使用價值,則可以使用 restoreTrash 命令將被刪除文件恢復到原路徑下:restoreTrash ~/trash/useless_20140923_06_28_57。具體執(zhí)行情況如圖 4 所示:
圖 4.restoreTrash 腳本執(zhí)行情況
查看/usr 目錄,可以發(fā)現(xiàn) useless 文件已經(jīng)被恢復至此。
圖 5.useless 文件被恢復
總結
本文仿照 Windows 中回收站的功能,在 Linux 中做了實現(xiàn),可以有效的防止由于誤刪而造成的損失。讀者只需要將四個腳本拷到/bin 目錄下,并配置 crontab 即可使用 Linux 版回收站。
English » | | | | | | | | |
Text-to-speech function is limited to 100 characters
1.用系統(tǒng)管理員登陸系統(tǒng)。
2.停止MySQL的服務。
Windows:運行net stop mysql關閉數(shù)據(jù)庫
3.進入命令窗口,然后進入 MySQL的安裝目錄,比如我的安裝目錄是c:\mysql,進入C:\mysql\bin
4.跳過權限檢查啟動MySQL,
c:\mysql\bin>mysqld-nt ––skip-grant-tables
或則:c:\mysql\bin>mysqld ––skip-grant-tables
mysqld.exe是微軟Windows MySQL
server數(shù)據(jù)庫服務器相關程序。mysqld-nt.exe是MySQL Daemon數(shù)據(jù)庫服務相關程序。
5.[未驗證]
重新打開一個窗口
進入c:\mysql\bin目錄,設置root的新MySQL數(shù)據(jù)庫密碼
c:\mysql\bin>mysqladmin -u root flush-privileges password "newpassword"
c:\mysql\bin>mysqladmin -u root -p shutdown
將newpassword替為自己的新密碼
English » | | | | | | | | |
Text-to-speech function is limited to 100 characters
一個老生常談的話題,有人說“
產(chǎn)品經(jīng)理就是不斷讓正確的事情發(fā)生”,又有人說“產(chǎn)品經(jīng)理要為團隊摸清方向,指明道路”。姑娘承認,這些說法都極為正確,但是今天想簡單和大家談談我眼中產(chǎn)品經(jīng)理如何才能把一件事情做出色。
站的多高 想的多遠
最近接手了一個新的外部合作項目。在普通的產(chǎn)品經(jīng)理眼中,目的是把項目在規(guī)定的期限內完成接入上線;高一層的產(chǎn)品經(jīng)理眼中,目的是把項目在規(guī)定的期限內完成接入上線,并擁有良好的用戶體驗;但優(yōu)秀的產(chǎn)品經(jīng)理的眼中,目的是把項目在規(guī)定的期限內完成接入上線,并擁有良好的用戶體驗,并且提出接入項目在產(chǎn)品整體發(fā)展規(guī)劃中的作用。
有點繞,不如舉例說明下。每個人是一個獨立的產(chǎn)品,現(xiàn)在需要和第三方去合作完成婚配問題。普通的產(chǎn)品經(jīng)理是在規(guī)定時間內和第三方完成婚配;高一層的產(chǎn)品經(jīng)理是在規(guī)定時間內和第三方完成婚配,并且提供雙方愉悅的
心情體驗;優(yōu)秀的產(chǎn)品經(jīng)理是在規(guī)定時間內和第三方完成婚配,并且提供雙方愉悅的心情體驗,同時為產(chǎn)品記錄雙方有價值的紀念日、地點、事情等信息。不僅能為現(xiàn)有用戶提供更好的服務(時光紀念冊、紀念日提醒等),拉長產(chǎn)品周期,同時,經(jīng)過大量的數(shù)據(jù)累積為其它用戶提供完成婚配的參考方案。
放的多低 走的多遠
產(chǎn)品經(jīng)理并沒有理想中光環(huán)。敲得了代碼,做得了運營,搞得了推廣,寫得了文案,弄得了分析。一個優(yōu)秀的產(chǎn)品經(jīng)理具有強大的責任感,所謂的責任感姑娘的理解是能把自己放的多低。給你的程序猿哥哥送杯水,給你的用戶解答產(chǎn)品疑問,給你的產(chǎn)品發(fā)布會一個霸氣的開場??傊睦镄枰腿ツ睦?,哪里要打替補你就得上,任何人都可以推,但你不行,那是你“兒子”。
不斷發(fā)現(xiàn)問題 并且解決問題
不斷發(fā)現(xiàn)問題,不單單是產(chǎn)品的問題,還有團隊的問題、流程的問題等等。把發(fā)現(xiàn)的所有問題歸納并進行解決。
以目的為導向 靈活變通
工作中發(fā)現(xiàn),往往會遇到這樣的情況,目的是這樣這樣的,我們構思的方法是這樣這樣的,然后發(fā)現(xiàn)行不通,就“噠噠噠”跑去告訴領導不行。其實目的是只有一個,但中間的方法總是可以變通的。有時候我們的思維總是很可怕,會陷入單一的模式中不能自拔。學會跳出固有思維,發(fā)散性的考慮問題會有更多的收獲。
在工作中我們往往會忽略掉很多,希望通過不斷的總結提醒自己,讓自己養(yǎng)成習慣。
English » | | | | | | | | |
Text-to-speech function is limited to 100 characters
我們討論了同步容器(Hashtable、Vector),也討論了并發(fā)容器(ConcurrentHashMap、CopyOnWriteArrayList),這些工具都為我們編寫多線程程序提供了很大的方便。今天我們來討論另外一類容器:阻塞隊列。
在前面我們接觸的隊列都是非阻塞隊列,比如PriorityQueue、LinkedList(LinkedList是雙向鏈表,它實現(xiàn)了Dequeue接口)。
使用非阻塞隊列的時候有一個很大問題就是:它不會對當前線程產(chǎn)生阻塞,那么在面對類似消費者-生產(chǎn)者的模型時,就必須額外地實現(xiàn)同步策略以及線程間喚醒策略,這個實現(xiàn)起來就非常麻煩。但是有了阻塞隊列就不一樣了,它會對當前線程產(chǎn)生阻塞,比如一個線程從一個空的阻塞隊列中取元素,此時線程會被阻塞直到阻塞隊列中有了元素。當隊列中有元素后,被阻塞的線程會自動被喚醒(不需要我們編寫代碼去喚醒)。這樣提供了極大的方便性。
本文先講述一下
java.util.concurrent包下提供主要的幾種阻塞隊列,然后分析了阻塞隊列和非阻塞隊列的中的各個方法,接著分析了阻塞隊列的實現(xiàn)原理,最后給出了一個實際例子和幾個使用場景。
一.幾種主要的阻塞隊列
自從Java 1.5之后,在java.util.concurrent包下提供了若干個阻塞隊列,主要有以下幾個:
ArrayBlockingQueue:基于數(shù)組實現(xiàn)的一個阻塞隊列,在創(chuàng)建ArrayBlockingQueue對象時必須制定容量大小。并且可以指定公平性與非公平性,默認情況下為非公平的,即不保證等待時間最長的隊列最優(yōu)先能夠訪問隊列。
LinkedBlockingQueue:基于鏈表實現(xiàn)的一個阻塞隊列,在創(chuàng)建LinkedBlockingQueue對象時如果不指定容量大小,則默認大小為Integer.MAX_VALUE。
PriorityBlockingQueue:以上2種隊列都是先進先出隊列,而PriorityBlockingQueue卻不是,它會按照元素的優(yōu)先級對元素進行排序,按照優(yōu)先級順序出隊,每次出隊的元素都是優(yōu)先級最高的元素。注意,此阻塞隊列為無界阻塞隊列,即容量沒有上限(通過源碼就可以知道,它沒有容器滿的信號標志),前面2種都是有界隊列。
DelayQueue:基于PriorityQueue,一種延時阻塞隊列,DelayQueue中的元素只有當其指定的延遲時間到了,才能夠從隊列中獲取到該元素。DelayQueue也是一個無界隊列,因此往隊列中插入數(shù)據(jù)的操作(生產(chǎn)者)永遠不會被阻塞,而只有獲取數(shù)據(jù)的操作(消費者)才會被阻塞。
二.阻塞隊列中的方法 VS 非阻塞隊列中的方法
1.非阻塞隊列中的幾個主要方法:
add(E e):將元素e插入到隊列末尾,如果插入成功,則返回true;如果插入失?。搓犃幸褲M),則會拋出異常;
remove():移除隊首元素,若移除成功,則返回true;如果移除失?。犃袨榭眨?,則會拋出異常;
offer(E e):將元素e插入到隊列末尾,如果插入成功,則返回true;如果插入失?。搓犃幸褲M),則返回false;
poll():移除并獲取隊首元素,若成功,則返回隊首元素;否則返回null;
peek():獲取隊首元素,若成功,則返回隊首元素;否則返回null
對于非阻塞隊列,一般情況下建議使用offer、poll和peek三個方法,不建議使用add和remove方法。因為使用offer、poll和peek三個方法可以通過返回值判斷操作成功與否,而使用add和remove方法卻不能達到這樣的效果。注意,非阻塞隊列中的方法都沒有進行同步措施。
2.阻塞隊列中的幾個主要方法:
阻塞隊列包括了非阻塞隊列中的大部分方法,上面列舉的5個方法在阻塞隊列中都存在,但是要注意這5個方法在阻塞隊列中都進行了同步措施。除此之外,阻塞隊列提供了另外4個非常有用的方法:
put(E e)
take()
offer(E e,long timeout, TimeUnit unit)
poll(long timeout, TimeUnit unit)
put方法用來向隊尾存入元素,如果隊列滿,則等待;
take方法用來從隊首取元素,如果隊列為空,則等待;
offer方法用來向隊尾存入元素,如果隊列滿,則等待一定的時間,當時間期限達到時,如果還沒有插入成功,則返回false;否則返回true;
poll方法用來從隊首取元素,如果隊列空,則等待一定的時間,當時間期限達到時,如果取到,則返回null;否則返回取得的元素;
三.阻塞隊列的實現(xiàn)原理
前面談到了非阻塞隊列和阻塞隊列中常用的方法,下面來探討阻塞隊列的實現(xiàn)原理,本文以ArrayBlockingQueue為例,其他阻塞隊列實現(xiàn)原理可能和ArrayBlockingQueue有一些差別,但是大體思路應該類似,有興趣的朋友可自行查看其他阻塞隊列的實現(xiàn)源碼。
首先看一下ArrayBlockingQueue類中的幾個成員變量:
public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable { private static final long serialVersionUID = -817911632652898426L; /** The queued items */ private final E[] items; /** items index for next take, poll or remove */ private int takeIndex; /** items index for next put, offer, or add. */ private int putIndex; /** Number of items in the queue */ private int count; /* * Concurrency control uses the classic two-condition algorithm * found in any textbook. */ /** Main lock guarding all access */ private final ReentrantLock lock; /** Condition for waiting takes */ private final Condition notEmpty; /** Condition for waiting puts */ private final Condition notFull; } |
可以看出,ArrayBlockingQueue中用來存儲元素的實際上是一個數(shù)組,takeIndex和putIndex分別表示隊首元素和隊尾元素的下標,count表示隊列中元素的個數(shù)。
lock是一個可重入鎖,notEmpty和notFull是等待條件。
下面看一下ArrayBlockingQueue的構造器,構造器有三個重載版本:
public ArrayBlockingQueue(int capacity) { } public ArrayBlockingQueue(int capacity, boolean fair) { } public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c) { } |
第一個構造器只有一個參數(shù)用來指定容量,第二個構造器可以指定容量和公平性,第三個構造器可以指定容量、公平性以及用另外一個集合進行初始化。
然后看它的兩個關鍵方法的實現(xiàn):put()和take():
public void put(E e) throws InterruptedException { if (e == null) throw new NullPointerException(); final E[] items = this.items; final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { try { while (count == items.length) notFull.await(); } catch (InterruptedException ie) { notFull.signal(); // propagate to non-interrupted thread throw ie; } insert(e); } finally { lock.unlock(); } } |
從put方法的實現(xiàn)可以看出,它先獲取了鎖,并且獲取的是可中斷鎖,然后判斷當前元素個數(shù)是否等于數(shù)組的長度,如果相等,則調用notFull.await()進行等待,如果捕獲到中斷異常,則喚醒線程并拋出異常。
當被其他線程喚醒時,通過insert(e)方法插入元素,最后解鎖。
我們看一下insert方法的實現(xiàn):
private void insert(E x) { items[putIndex] = x; putIndex = inc(putIndex); ++count; notEmpty.signal(); } |
它是一個private方法,插入成功后,通過notEmpty喚醒正在等待取元素的線程。
下面是take()方法的實現(xiàn):
public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { try { while (count == 0) notEmpty.await(); } catch (InterruptedException ie) { notEmpty.signal(); // propagate to non-interrupted thread throw ie; } E x = extract(); return x; } finally { lock.unlock(); } } |
跟put方法實現(xiàn)很類似,只不過put方法等待的是notFull信號,而take方法等待的是notEmpty信號。在take方法中,如果可以取元素,則通過extract方法取得元素,下面是extract方法的實現(xiàn):
private E extract() {
final E[] items = this.items;
E x = items[takeIndex];
items[takeIndex] = null;
takeIndex = inc(takeIndex);
--count;
notFull.signal();
return x;
}
跟insert方法也很類似。
其實從這里大家應該明白了阻塞隊列的實現(xiàn)原理,事實它和我們用Object.wait()、Object.notify()和非阻塞隊列實現(xiàn)生產(chǎn)者-消費者的思路類似,只不過它把這些工作一起集成到了阻塞隊列中實現(xiàn)。
四.示例和使用場景
下面先使用Object.wait()和Object.notify()、非阻塞隊列實現(xiàn)生產(chǎn)者-消費者模式:
public class Test { private int queueSize = 10; private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize); public static void main(String[] args) { Test test = new Test(); Producer producer = test.new Producer(); Consumer consumer = test.new Consumer(); producer.start(); consumer.start(); } class Consumer extends Thread{ @Override public void run() { consume(); } private void consume() { while(true){ synchronized (queue) { while(queue.size() == 0){ try { System.out.println("隊列空,等待數(shù)據(jù)"); queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); queue.notify(); } } queue.poll(); //每次移走隊首元素 queue.notify(); System.out.println("從隊列取走一個元素,隊列剩余"+queue.size()+"個元素"); } } } } class Producer extends Thread{ @Override public void run() { produce(); } private void produce() { while(true){ synchronized (queue) { while(queue.size() == queueSize){ try { System.out.println("隊列滿,等待有空余空間"); queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); queue.notify(); } } queue.offer(1); //每次插入一個元素 queue.notify(); System.out.println("向隊列取中插入一個元素,隊列剩余空間:"+(queueSize-queue.size())); } } } } } |
這個是經(jīng)典的生產(chǎn)者-消費者模式,通過阻塞隊列和Object.wait()和Object.notify()實現(xiàn),wait()和notify()主要用來實現(xiàn)線程間通信。
具體的線程間通信方式(wait和notify的使用)在后續(xù)問章中會講述到。
下面是使用阻塞隊列實現(xiàn)的生產(chǎn)者-消費者模式:
public class Test { private int queueSize = 10; private ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(queueSize); public static void main(String[] args) { Test test = new Test(); Producer producer = test.new Producer(); Consumer consumer = test.new Consumer(); producer.start(); consumer.start(); } class Consumer extends Thread{ @Override public void run() { consume(); } private void consume() { while(true){ try { queue.take(); System.out.println("從隊列取走一個元素,隊列剩余"+queue.size()+"個元素"); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Producer extends Thread{ @Override public void run() { produce(); } private void produce() { while(true){ try { queue.put(1); System.out.println("向隊列取中插入一個元素,隊列剩余空間:"+(queueSize-queue.size())); } catch (InterruptedException e) { e.printStackTrace(); } } } } } |
有沒有發(fā)現(xiàn),使用阻塞隊列代碼要簡單得多,不需要再單獨考慮同步和線程間通信的問題。
在并發(fā)編程中,一般推薦使用阻塞隊列,這樣實現(xiàn)可以盡量地避免程序出現(xiàn)意外的錯誤。
阻塞隊列使用最經(jīng)典的場景就是socket客戶端數(shù)據(jù)的讀取和解析,讀取數(shù)據(jù)的線程不斷將數(shù)據(jù)放入隊列,然后解析線程不斷從隊列取數(shù)據(jù)解析。還有其他類似的場景,只要符合生產(chǎn)者-消費者模型的都可以使用阻塞隊列。
English » | | | | | | | | |
Text-to-speech function is limited to 100 characters