從事一個項目,須要考慮數據的安全性,以前對於數據庫這部分的數據操做學習的比較零散,因爲手頭的項目,因而系統的html
學習了下數據庫操做加鎖的知識:java
--------------------------------------------------華麗麗的分割線-----------------------------------------------------------mysql
學習一個知識,咱們大體都會經歷這麼幾個過程(what this ? why to use ? how to use?),首先,咱們須要搞懂,下面幾個知識點:算法
一: 什麼是數據庫加鎖 ?sql
數據庫加鎖: 簡單的意思就是對於在執行一個操做(好比修改)時,對這個操做的對象加鎖,放置其餘操做讀取到髒數據或者幽靈數據。數據庫
或者術語來講就是一種排他鎖,當寫的時候不容許其餘程序寫,這樣就能夠保證數據一致性了安全
二:爲何要給數據加鎖?多線程
對於這點,咱們須要簡單的瞭解幾個概念:ide
(1).什麼是事務?學習
事務: 是用戶定義的數據庫操做系列,這些操做做爲一個完整的工做單元執行。一個事務內的全部語句做爲一個總體。要麼所有執行,要麼所有不執行。
事務的幾個特色: 原子性,一致性,隔離性,持久性, 簡稱ACID特徵
通常來說: 事務的這幾個特色被遭到破壞的有下列幾個狀況:
(1) 多事務並行運行時,不一樣事務的操做有交叉狀況。(->_-> 若是你想不到列子:就想一想多線程問題)
(2) 事務在運行過程當中被強迫中止。
(2)什麼是髒讀:
髒讀 :髒讀就是指當一個事務正在訪問數據,而且對數據進行了修改,而這種修改尚未提交到數據庫中,這時,另一個事務也訪問這個數據,而後使用了這個數據。
(3)什麼是不可重複讀?
不可重複讀 :是指在一個事務內,屢次讀同一數據。在這個事務尚未結束時,另一個事務也訪問該同一數據。那麼,在第一個事務中的兩次讀數據之間,因爲第二個事務的修改,那麼第一個事務兩次讀到的的數據多是不同的。這樣就發生了在一個事務內兩次讀到的數據是不同的,所以稱爲是不可重複讀。例如,一個編輯人員兩次讀取同一文檔,但在兩次讀取之間,做者重寫了該文檔。當編輯人員第二次讀取文檔時,文檔已更改。原始讀取不可重複。若是隻有在做者所有完成編寫後編輯人員才能夠讀取文檔,則能夠避免該問題。
(4)什麼是幻讀?
幻讀 : 是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,這種修改涉及到表中的所有數據行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那麼,之後就會發生操做第一個事務的用戶發現表中還有沒有修改的數據行,就好象發生了幻覺同樣。例如,一個編輯人員更改做者提交的文檔,但當生產部門將其更改內容合併到該文檔的主複本時,發現做者已將未編輯的新材料添加到該文檔中。若是在編輯人員和生產部門完成對原始文檔的處理以前,任何人都不能將新材料添加到文檔中,則能夠避免該問題。
由於在上述的狀況下,數據會出現髒數據。對於一個考慮安全性的系統而言,加鎖天然是十分必要.
(三)如何對數據加鎖:
對於數據加鎖: 通常分爲以下兩種,第一類,就是數據庫本身加鎖,第二類,就是線程鎖。
第一種: 數據庫本身加鎖
對於鎖的級別: 庫級鎖,表級鎖,頁級鎖,行級鎖。(這篇文章提供了較多的說明)
http://blog.csdn.net/cspyb/article/details/1865538
舉幾個列子:對於數據表的加鎖:
方法一: 使用SQL語句進行加鎖
public void test() { String sql = "lock tables Gxjun write"; // 或String sql = "lock tables Gxjun read"; // 若是想鎖多個表 lock tables Gxjun read stu write , ..... String sql1 = "select * from Gxjun "; String sql2 = "unlock tables"; try { this.pstmt = conn.prepareStatement(sql); this.pstmt1 = conn.prepareStatement(sql1); this.pstmt2 = conn.prepareStatement(sql2); pstmt.executeQuery(); pstmt1.executeQuery(); pstmt2.executeQuery(); } catch (Exception e) { System.out.println("異常" + e.getMessage()); } }
方法二 , 採用記錄鎖加鎖:
public void test() { String sql = "select * from Gxjun for update"; try { conn.setAutoCommit(false); this.pstmt = conn.prepareStatement(sql); pstmt.executeQuery(); } catch (Exception e) { System.out.println("異常" + e.getMessage()); } }
須要標註的幾點就是:(摘錄自思緒飛帆,鳴謝----思緒飛帆)
/* 1.for update 與 lock in share mode 屬於行級鎖和頁級鎖 2.for update 排它鎖,lock in share mode 共享鎖 3.對於記錄鎖.必須開啓事務. 4.行級鎖定事實上是索引記錄的鎖定.只要是用索引掃描的行(或沒索引全表掃描的行),都將被鎖住. 5.在不一樣的隔離級別下還會使用next-key locking算法.即所掃描的行之間的「間隙」也會也鎖住(在Repeatable read和Serializable隔離級別下有間隙鎖). 6.在mysql中共享鎖的含義是:在被共享鎖鎖住的行,即便內容被修改且並無提交.在另外一個會話中依然看到最新修改的信息. 在同一會話中加上了共享鎖.能夠對這個表以及這個表之外的全部表進行增、刪、改、查的操做. 在不一樣的會話中.能夠查到共享鎖鎖住行的最新消息.可是在Read Uncommitted隔離級別下不能對鎖住的表進行刪, 改操做.(須要等待鎖釋放才能操做...) 在Read Committed隔離級別下不能對鎖住的表進行刪,改操做.(須要等待鎖釋放才能操做...) 在Repeatable read隔離級別下不能對鎖住行進行增、刪、改操做.(須要等待鎖釋放才能操做...) 在Serializable隔離級別下不能對鎖住行進行增、刪、改操做. (須要等待鎖釋放才能操做...) 7.在mysql中排他鎖的含義是:在被排它鎖鎖住的行,內容修改並沒提交,在另外一個會話中不會看到最新修改的信息。 在不一樣的會話中.能夠查到共享鎖鎖住行的最新消息.可是Read Uncommitted隔離級別下不能對鎖住的表進行刪, 改操做.(須要等待鎖釋放才能操做...) 在Read Committed隔離級別下不能對鎖住的表進行刪,改操做.(須要等待鎖釋放才能操做...) 在Repeatable read隔離級別下不能對鎖住行進行增、刪、改操做.(須要等待鎖釋放才能操做...) 在Serializable隔離級別下不能對鎖住行進行增、刪、改操做. (須要等待鎖釋放才能操做...) 8.在同一個會話中的能夠疊加多個共享鎖和排他鎖.在多個會話中,須要等待鎖的釋放. 9.SQL中的update 與 for update是同樣的原理. 10.等待超時的參數設置:innodb_lock_wait_timeout=50 (單位秒). 11.任何能夠觸發事務提交的命令,均可以關閉共享鎖和排它鎖. */
第二種: 就是線程鎖,這是我重點學習的地方,(額,需呀註解的是,是我重點學習的地方,而後每一個人注重點不一樣)
引用到的材料:
1. http://lavasoft.blog.51cto.com/62575/99155
2.http://www.cnblogs.com/hoojo/archive/2011/05/05/2038101.html
3. http://www.blogjava.net/zhangwei217245/archive/2010/04/08/315526.html
-------------------------------------------------------------------------------------------------------------------------------------- 鳴謝上述做者
0-----------------------------------------------------------------華麗麗的分割線----------------------------------------------------------------------0
舉列子:
在不採用同步鎖的狀況下
1 package Day_2; 2 3 import java.util.concurrent.ExecutorService; 4 import java.util.concurrent.Executors; 5 /** 6 * @author Gxjun 7 * 功能:同步鎖 8 */ 9 public class Demo_tongbusuo { 10 public static void main(String args []){ 11 MyThread r = new MyThread(); 12 //ExecutorService MyThreadPool = Executors.newCachedThreadPool(); 13 Thread ta = new Thread(r, "王小二"); 14 Thread tb = new Thread(r, "王小三"); 15 //加載線程池中去 16 // MyThreadPool.execute(ta); 17 //MyThreadPool.execute(tb); 18 ta.start(); 19 tb.start(); 20 } 21 } 22 23 class Foo { 24 private int x = 100; 25 26 public int getX() { 27 return x; 28 } 29 30 public int fix(int y) { 31 x = x - y; 32 return x; 33 } 34 } 35 36 class MyThread implements Runnable { 37 private Foo foo = new Foo(); 38 39 public void run() { 40 for (int i = 0; i < 3; i++) { 41 this.fix(30); 42 try { 43 Thread.sleep(1); 44 } catch (InterruptedException e) { 45 e.printStackTrace(); 46 } 47 System.out.println(Thread.currentThread().getName() + " : 當前foo對象的x值= " + foo.getX()); 48 } 49 } 50 51 public int fix(int y) { 52 return foo.fix(y); 53 } 54 }
結果爲:
王小二 : 當前foo對象的x值= 40
王小三 : 當前foo對象的x值= 40
王小二 : 當前foo對象的x值= -20
王小三 : 當前foo對象的x值= -50
王小二 : 當前foo對象的x值= -80
王小三 : 當前foo對象的x值= -80
可是對於上述問題,採用線程池,卻能獲得完滿的解決。
將其改爲同步鎖以後:
1 package Day_2; 2 3 import java.util.concurrent.ExecutorService; 4 import java.util.concurrent.Executors; 5 /** 6 * @author Gxjun 7 * 功能:同步鎖 8 */ 9 public class Demo_tongbusuo { 10 public static void main(String args []){ 11 MyThread r = new MyThread(); 12 //ExecutorService MyThreadPool = Executors.newCachedThreadPool(); 13 Thread ta = new Thread(r, "王小二"); 14 Thread tb = new Thread(r, "王小三"); 15 //加載線程池中去 16 // MyThreadPool.execute(ta); 17 //MyThreadPool.execute(tb); 18 ta.start(); 19 tb.start(); 20 } 21 } 22 23 class Foo { 24 private int x = 100; 25 26 public int getX() { 27 //改成同步鎖 28 synchronized (this) { 29 return x; 30 } 31 } 32 33 public int fix(int y) { 34 x = x - y; 35 return x; 36 } 37 } 38 39 class MyThread implements Runnable { 40 private Foo foo = new Foo(); 41 42 public void run() { 43 for (int i = 0; i < 3; i++) { 44 this.fix(30); 45 try { 46 Thread.sleep(1); 47 } catch (InterruptedException e) { 48 e.printStackTrace(); 49 } 50 System.out.println(Thread.currentThread().getName() + " : 當前foo對象的x值= " + foo.getX()); 51 } 52 } 53 54 public int fix(int y) { 55 return foo.fix(y); 56 } 57 }
結果:
王小二 : 當前foo對象的x值= 40
王小三 : 當前foo對象的x值= 40
王小三 : 當前foo對象的x值= -20
王小二 : 當前foo對象的x值= -20
王小三 : 當前foo對象的x值= -80
王小二 : 當前foo對象的x值= -80
這個結果和採用線程池獲得結果是同樣的........
採用非同步鎖結果也是同樣的.....
1 package Day_2; 2 3 import java.util.concurrent.ExecutorService; 4 import java.util.concurrent.Executors; 5 /** 6 * @author Gxjun 7 * 功能:同步鎖 8 */ 9 public class Demo_tongbusuo { 10 public static void main(String args []){ 11 MyThread r = new MyThread(); 12 //ExecutorService MyThreadPool = Executors.newCachedThreadPool(); 13 Thread ta = new Thread(r, "王小二"); 14 Thread tb = new Thread(r, "王小三"); 15 //加載線程池中去 16 // MyThreadPool.execute(ta); 17 //MyThreadPool.execute(tb); 18 ta.start(); 19 tb.start(); 20 } 21 } 22 23 class Foo { 24 private int x = 100; 25 26 public synchronized int getX() { 27 return x; 28 } 29 30 public int fix(int y) { 31 x = x - y; 32 return x; 33 } 34 } 35 36 class MyThread implements Runnable { 37 private Foo foo = new Foo(); 38 39 public void run() { 40 for (int i = 0; i < 3; i++) { 41 this.fix(30); 42 try { 43 Thread.sleep(1); 44 } catch (InterruptedException e) { 45 e.printStackTrace(); 46 } 47 System.out.println(Thread.currentThread().getName() + " : 當前foo對象的x值= " + foo.getX()); 48 } 49 } 50 51 public int fix(int y) { 52 return foo.fix(y); 53 } 54 }