一、锁的原理

  Java中每个对象都有一个内置锁,当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行的代码类的当前实例(this实例)有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。

  当程序运行到synchronized同步方法或代码块时该对象锁才起作用。一个对象只有一个锁。所以一个线程获得该所,就没有其他线程获得,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。释放锁是指持锁线程退出synchronized同步方法或代码块。

2、注意事项

   1) 只能同步方法,不能同步变量和类。

   2) 每个对象只有一个锁,所以应该清楚在哪一个对象上同步。

   3) 不必同步类的所有方法,类可以同时拥有同步和非同步方法。

   4) 如果向拥有同步和非同步方法,则非同步方法可以被多个线程自由访问不受锁的限制。

   5) 线程睡眠时,它所持的锁不会释放。

   6) 线程可以获得多个锁。比如在一个对象的同步方法里面调用另一个对象的同步方法,则获得了两个对象的同步锁。

   7) 同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。

   8) 使用同步代码块时,应该指出在哪个对象上同步,也就是说要获得哪个对象的锁,如

1 public int fix(int y){
2 synchronized(this){
3 x=x-y;
4 }
5 return x;
6 }

二、如果线程不能获得锁会怎么样

  如果线程试图进入同步方法,而锁被其他线程占用,则该线程被阻塞。实际上,线程进入该对象的一种池中,必须在那里等待,直到其所被释放。

当考虑堵塞时,一定要注意哪个对象正在被用于锁定:

  1、调用用一个对象中非静态同步方法的线程将被堵塞。如果是不同对象,则线程之间互不干扰。

  2、调用同一个类中的静态同步方法的线程将被堵塞,它们都是锁定在相同的Cass对象上。

  3、静态同步方法和非静态同步方法将永远不会彼此堵塞,因为静态方法锁定在Class对象上,非静态方法锁定在该类的对象上。

  4、对于同步代码块,要看清楚什么对象已经用于锁定(synchronized后面括号的内容)。在同一个对象上进行同步的线程将被堵塞,在不同对象上的线程永远不会被堵塞。

三、锁对象

  Java5中,提供了锁对象,利用锁对象可以实现资源的封锁,用来控制对竞争资源并发访问的控制,这些内容主要集中在java.util.concurrent.locks包下,主要有三个接口Condition、Lock、ReadEWriteLock。

 Condition接口:
Condition将Object监视器方法(wait、notify和notifyAll)分解成截然不同的对象,以便
通过将这些对象与任意的Lock实现组合使用,为每个对象提供多个等待set(wait-set)。
Lock接口:
Lock实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作。
ReadWriteLock接口:
ReadWriteLock维护了一对相关的锁定,一个用于只读操作,另一个用于写入操作。
下面是读写锁的必要步骤:
1)构造一个ReentrantReadWriteLock对象:
private ReentrantReadWriteLock rwl=new ReentrantReadWriteLock()
2)抽取读写锁:
private Lock readLock=rwl.readLock()
private Lock writeLock=rwl.writeLock()
3)对所有的获取方法加读锁:
public double getTotalBalance(){
readLock.lock()
try{...}
finally{readLock.unlock()}
}
4)对所有的修改方法加写锁:
public double transfer(){
writeLock.lock()
try{...}
finally{writeLock.unlock()}
}

具体看个例子: 

  LockTest.java

 package Thread;

 import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; /*
* Java线程:锁
*/
public class LockTest {
public static void main(String[] args){
MyCount myCount=new MyCount("955464",10000);//创建并发访问的账户
Lock lock=new ReentrantLock();//创建一个所对象
ExecutorService pool=Executors.newCachedThreadPool();//创建一个线程池
User1 u1=new User1("张三",myCount,-4000,lock);
User1 u2=new User1("李四",myCount,6000,lock);
User1 u3=new User1("王二",myCount,-8000,lock);
User1 u4=new User1("麻子",myCount,800,lock);
//在线程池中执行各个用户的操作
pool.execute(u1);
pool.execute(u2);
pool.execute(u3);
pool.execute(u4);
pool.shutdown();//关闭线程池
}
}
class User1 implements Runnable{
private String name;//用户名
private MyCount myCount;//所要操作的账户
private int iocash;//操作的余额,有正有负
private Lock myLock;//执行操作所需的锁对象
User1(String name,MyCount myCount,int iocash,Lock myLock){
this.name=name;
this.myCount=myCount;
this.iocash=iocash;
this.myLock=myLock;
}
public void run(){
myLock.lock();//获取锁
System.out.println(name+"正在操作"+myCount+"账户,金额为:"+iocash+",当前金额为:"+
myCount.getCash());//执行现金任务
myCount.setCash(myCount.getCash()+iocash);
System.out.println(name+"操作"+myCount+"账户成功,金额为:"+iocash+",当前金额为:"+
myCount.getCash());
myLock.unlock();//释放锁,否则别的线程没有机会执行
}
}
class MyCount{
private String oid;//账户
private int cash;//余额
MyCount(String oid,int cash){
this.oid=oid;
this.cash=cash;
}
public String getOid(){
return oid;
}
public void setOid(String oid){
this.oid=oid;
}
public int getCash(){
return cash;
}
public void setCash(int cash){
this.cash=cash;
}
public String toString(){
return "MyCount{oid="+oid+",cash="+cash+"}";
}
}

  结果为:

 张三正在操作MyCount{oid=955464,cash=10000}账户,金额为:-4000,当前金额为:10000
张三操作MyCount{oid=955464,cash=6000}账户成功,金额为:-4000,当前金额为:6000
李四正在操作MyCount{oid=955464,cash=6000}账户,金额为:6000,当前金额为:6000
李四操作MyCount{oid=955464,cash=12000}账户成功,金额为:6000,当前金额为:12000
王二正在操作MyCount{oid=955464,cash=12000}账户,金额为:-8000,当前金额为:12000
王二操作MyCount{oid=955464,cash=4000}账户成功,金额为:-8000,当前金额为:4000
麻子正在操作MyCount{oid=955464,cash=4000}账户,金额为:800,当前金额为:4000
麻子操作MyCount{oid=955464,cash=4800}账户成功,金额为:800,当前金额为:4800

  上述例子是普通的锁,不区分读写,在这里,为了提高性能,读的地方用读锁,写的地方用写锁,提高了执行效率。平时的时候尽量写读写锁,不用普通锁。

  LockTest.java

 package Thread;

 import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; /*
* Java线程:锁
*/
public class LockTest {
public static void main(String[] args){
MyCount myCount=new MyCount("955464",10000);//创建并发访问的账户
ReadWriteLock lock=new ReentrantReadWriteLock(false);//创建一个所对象
ExecutorService pool=Executors.newCachedThreadPool();//创建一个线程池
User1 u1=new User1("张三",myCount,-4000,lock,false);
User1 u2=new User1("李四",myCount,6000,lock,false);
User1 u3=new User1("王二",myCount,-8000,lock,false);
User1 u4=new User1("麻子",myCount,800,lock,false);
User1 u5=new User1("麻子它姐",myCount,0,lock,true);
//在线程池中执行各个用户的操作
pool.execute(u1);
pool.execute(u2);
pool.execute(u3);
pool.execute(u4);
pool.execute(u5);
pool.shutdown();//关闭线程池
}
}
class User1 implements Runnable{
private String name;//用户名
private MyCount myCount;//所要操作的账户
private int iocash;//操作的余额,有正有负
private ReadWriteLock myLock;//执行操作所需的锁对象
private boolean ischeck;//是否查询
User1(String name,MyCount myCount,int iocash,ReadWriteLock myLock,boolean ischeck){
this.name=name;
this.myCount=myCount;
this.iocash=iocash;
this.myLock=myLock;
this.ischeck=ischeck;
}
public void run(){
if(ischeck){
myLock.readLock().lock();//获取锁
System.out.println("读:"+name+"正在查询"+myCount+",当前金额为:"+
myCount.getCash());//执行现金任务
myLock.readLock().unlock();
}else{
myLock.writeLock().lock();
System.out.println("写:"+name+"正在操作"+myCount+"账户,金额为:"+iocash+",当前金额为:"+
myCount.getCash());//执行现金任务
myCount.setCash(myCount.getCash()+iocash);
System.out.println("写:"+name+"操作"+myCount+"账户成功,金额为:"+iocash+",当前金额为:"+
myCount.getCash());
myLock.writeLock().unlock();//释放锁,否则别的线程没有机会执行
}
}
}
class MyCount{
private String oid;//账户
private int cash;//余额
MyCount(String oid,int cash){
this.oid=oid;
this.cash=cash;
}
public String getOid(){
return oid;
}
public void setOid(String oid){
this.oid=oid;
}
public int getCash(){
return cash;
}
public void setCash(int cash){
this.cash=cash;
}
public String toString(){
return "MyCount{oid="+oid+",cash="+cash+"}";
}
}

  结果为:

 写:张三正在操作MyCount{oid=955464,cash=10000}账户,金额为:-4000,当前金额为:10000
写:张三操作MyCount{oid=955464,cash=6000}账户成功,金额为:-4000,当前金额为:6000
写:王二正在操作MyCount{oid=955464,cash=6000}账户,金额为:-8000,当前金额为:6000
写:王二操作MyCount{oid=955464,cash=-2000}账户成功,金额为:-8000,当前金额为:-2000
写:李四正在操作MyCount{oid=955464,cash=-2000}账户,金额为:6000,当前金额为:-2000
写:李四操作MyCount{oid=955464,cash=4000}账户成功,金额为:6000,当前金额为:4000
读:麻子它姐正在查询MyCount{oid=955464,cash=4000},当前金额为:4000
写:麻子正在操作MyCount{oid=955464,cash=4000}账户,金额为:800,当前金额为:4000
写:麻子操作MyCount{oid=955464,cash=4800}账户成功,金额为:800,当前金额为:4800

四、死锁

  死锁发生的可能性很小,即使看似死锁的代码,运行时也不一定产生死锁,发生死锁的原因是:当两个线程被堵塞, 每个线程在等待另一个线程时发生死锁,一般是两个对象的锁相互等待造成的。具体例子:

  DeathLockTest.java

 package Thread;

 public class DeathLockTest {
public static void main(String[] args){
DeadlockRisk dead=new DeadlockRisk();
MyThread1 t1=new MyThread1(dead,1,2);
MyThread1 t2=new MyThread1(dead,3,4);
MyThread1 t3=new MyThread1(dead,5,6);
MyThread1 t4=new MyThread1(dead,7,8);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class MyThread1 extends Thread{
private DeadlockRisk dead;
private int a,b;
MyThread1(DeadlockRisk dead,int a,int b){
this.dead=dead;
this.a=a;
this.b=b;
}
public void run(){
dead.read();
dead.write(a,b);
}
}
class DeadlockRisk{
private static class Resource{
public int value;
}
private Resource resourceA=new Resource();
private Resource resourceB=new Resource();
public int read(){
synchronized (resourceA){
System.out.println("read():"+Thread.currentThread().getName()+"获取了resourceA的锁!");
synchronized (resourceB){
System.out.println("read():"+Thread.currentThread().getName()+"获取了resourceB的锁!");
return resourceB.value+resourceA.value;
}
}
}
public void write(int a,int b){
synchronized (resourceB){
System.out.println("write():"+Thread.currentThread().getName()+"获取了resourceA的锁!");
synchronized (resourceA){
System.out.println("write():"+Thread.currentThread().getName()+"获取了resourceB的锁!");
resourceB.value=b;
resourceA.value=a;
}
}
}
}

  结果为:

 read():Thread-0获取了resourceA的锁!
read():Thread-0获取了resourceB的锁!
write():Thread-0获取了resourceA的锁!
read():Thread-3获取了resourceA的锁!

  这时,产生了死锁,程序不能继续运行了,但是如果修改一下,就能避免死锁。

  DeathLockTest.java

 package Thread;

 public class DeathLockTest {
public static void main(String[] args){
DeadlockRisk dead=new DeadlockRisk();
MyThread1 t1=new MyThread1(dead,1,2);
MyThread1 t2=new MyThread1(dead,3,4);
MyThread1 t3=new MyThread1(dead,5,6);
MyThread1 t4=new MyThread1(dead,7,8);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class MyThread1 extends Thread{
private DeadlockRisk dead;
private int a,b;
MyThread1(DeadlockRisk dead,int a,int b){
this.dead=dead;
this.a=a;
this.b=b;
}
public void run(){
dead.read();
dead.write(a,b);
}
}
class DeadlockRisk{
private static class Resource{
public int value;
}
private Resource resourceA=new Resource();
private Resource resourceB=new Resource();
public int read(){
synchronized (resourceA){
System.out.println("read():"+Thread.currentThread().getName()+"获取了resourceA的锁!");
synchronized (resourceB){
System.out.println("read():"+Thread.currentThread().getName()+"获取了resourceB的锁!");
return resourceB.value+resourceA.value;
}
}
}
public void write(int a,int b){
synchronized (resourceA){
System.out.println("write():"+Thread.currentThread().getName()+"获取了resourceA的锁!");
synchronized (resourceB){
System.out.println("write():"+Thread.currentThread().getName()+"获取了resourceB的锁!");
resourceB.value=b;
resourceA.value=a;
}
}
}
}

  结果为:

 read():Thread-0获取了resourceA的锁!
read():Thread-0获取了resourceB的锁!
read():Thread-3获取了resourceA的锁!
read():Thread-3获取了resourceB的锁!
write():Thread-3获取了resourceA的锁!
write():Thread-3获取了resourceB的锁!
read():Thread-2获取了resourceA的锁!
read():Thread-2获取了resourceB的锁!
write():Thread-2获取了resourceA的锁!
write():Thread-2获取了resourceB的锁!
read():Thread-1获取了resourceA的锁!
read():Thread-1获取了resourceB的锁!
write():Thread-1获取了resourceA的锁!
write():Thread-1获取了resourceB的锁!
write():Thread-0获取了resourceA的锁!
write():Thread-0获取了resourceB的锁!

Java线程:锁的更多相关文章

  1. JAVA线程锁-读写锁

    JAVA线程锁,除Lock的传统锁,又有两种特殊锁,叫读写锁ReadWriteLock 其中多个读锁不互斥,读锁和写锁互斥,写锁和写锁互斥 例子: /** * java线程锁分为读写锁 ReadWri ...

  2. Java线程锁一个简单Lock

    /** * @author * * Lock 是java.util.concurrent.locks下提供的java线程锁,作用跟synchronized类似, * 单是比它更加面向对象,两个线程执行 ...

  3. [置顶] 深入探析Java线程锁机制

    今天在iteye上提了一个关于++操作和线程安全的问题,一位朋友的回答一言点醒梦中人,至此我对Java线程锁有了更加深刻的认识.在这里也做个总结供大家参考. 先看几段代码吧! 代码一: public  ...

  4. Java线程锁&分布式锁的理解及应用

    了解Java线程锁之前,先理解线程和进程的定义.进程是操作系统分配资源(CPU)的基本单位,线程是CPU执行的基本单位,一个进程可拥有多个线程,同进程间的多个线程共享分配给进程的资源.比如启动JVM时 ...

  5. 工作常用4种Java线程锁的特点,性能比较、使用场景

    多线程的缘由 在出现了进程之后,操作系统的性能得到了大大的提升.虽然进程的出现解决了操作系统的并发问题,但是人们仍然不满足,人们逐渐对实时性有了要求. 使用多线程的理由之一是和进程相比,它是一种非常花 ...

  6. Java线程锁,synchronized、wait、notify详解

    (原) JAVA多线程这一块有点绕,特别是对于锁,对锁机制理解不清的话,程序出现了问题也很难找到原因,在此记录一下线程的执行以及各种锁. 1.JAVA中,每个对象有且只有一把锁(lock),也叫监视器 ...

  7. JAVA线程锁-读写锁应用,简单的缓存系统

    在JAVA1.5版本以后,JAVA API中提供了ReadWriteLock,此类是一个接口,在它的实现类中ReentrantReadWriteLock中有这样一段代码 class CachedDat ...

  8. Java 线程锁机制 -Synchronized Lock 互斥锁 读写锁

    (1)synchronized 是互斥锁: (2)ReentrantLock 顾名思义 :可重入锁 (3)ReadWriteLock :读写锁 读写锁特点: a)多个读者可以同时进行读b)写者必须互斥 ...

  9. JAVA线程锁lock下Condition的使用

    import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.uti ...

  10. java线程锁

    一.synchronized         这货可以锁对象,锁变量,锁方法,锁代码,好像什么都能锁,缺点就是如果一个锁堵了,其他的只能等待忙并不能把当前的锁给释放.二. ReentrantLockR ...

随机推荐

  1. Android——apk反编译

    一.工具准备: 1.dex2jar:http://code.google.com/p/dex2jar/downloads/list 2.JD-GUI:windows:http://laichao.go ...

  2. 计算机学院大学生程序设计竞赛(2015’12) 1002 Polygon

    #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #inclu ...

  3. POJ 1696 Space Ant

    极角排序 每次选择一个最外围的没选过的点,选择的时候需要利用极角排序进行选择 #include<cstdio> #include<cstring> #include<ve ...

  4. arcengine C#关于动态添加图层

    动态加载影像图层为例 研究了两三天算是弄出来了.本例适合影像数据量特别的大程序使用,可以动态的添加删除影像数据,还有不完善的地方,不过基本满足要求. 1.首先得到关键点的图层 m_Map = axMa ...

  5. Monitorix 监控 安装配置

    Monitorix 监控 安装配置 1. 首先安装RPMforge RPMforge 是由 Dag 及其他包裝者合作維護的.他們為 CentOS 提供超過 5000 個套件,包括 wine.vlc.m ...

  6. MySQL 索引的使用

    一.or 的使用 (1)MySQL版本大于 5.x 的会使用 index merge 功能,即可以将多个单列索引集合起来使用,不过在查询时使用 or 的话,引擎为 myisam 的会开启 index ...

  7. SQL语句详细汇总

    SQL语句详细汇总 | 浏览:3061 | 更新:2013-06-10 19:50 一.基础 1.说明:创建数据库 CREATE DATABASE database-name 2.说明:删除数据库 d ...

  8. LPC1788的spi使用

    #ifndef __SPI_H_ #define __SPI_H_ #include "common.h" #include "delay.h" // cs p ...

  9. iOS Socket第三方开源类库 ----AsyncSocket

    假如你也是一个java程序员,而你又不是很懂Socket. 下面我的这篇文章也许能帮助你一些. http://xiva.iteye.com/blog/993336 首先我们写好上面文章中的server ...

  10. Linux cronolog

    1. 关于本文    本文将以cronolog 1.6.2.apache 2.2.6为例,以CentOS 5为平台,讲述cronolog的安装和设置. 2. 关于cronolog     cronol ...