从事一个项目,须要考虑数据的安全性,以前对于数据库这部分的数据操做学习的比较零散,因为手头的项目,因而系统的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 }