`
nowing
  • 浏览: 84796 次
  • 来自: ...
社区版块
存档分类
最新评论

Java线程:锁

阅读更多
http://lavasoft.blog.51cto.com/62575/222084
http://lavasoft.blog.51cto.com/62575/222433

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

Condition Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set (wait-set)。
Lock Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
ReadWriteLock ReadWriteLock 维护了一对相关的锁定,一个用于只读操作,另一个用于写入操作。

有关锁的介绍,API文档解说很多,看得很烦,还是看个例子再看文档比较容易理解。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* Java线程:锁
*
* @author leizhimin 2009-11-5 10:57:29
*/
public class Test {
        public static void main(String[] args) {
                //创建并发访问的账户
                MyCount myCount = new MyCount("95599200901215522", 10000);
                //创建一个锁对象
                Lock lock = new ReentrantLock();
                //创建一个线程池
                ExecutorService pool = Executors.newCachedThreadPool();
                //创建一些并发访问用户,一个信用卡,存的存,取的取,好热闹啊
                User u1 = new User("张三", myCount, -4000, lock);
                User u2 = new User("张三他爹", myCount, 6000, lock);
                User u3 = new User("张三他弟", myCount, -8000, lock);
                User u4 = new User("张三", myCount, 800, lock);
                //在线程池中执行各个用户的操作
                pool.execute(u1);
                pool.execute(u2);
                pool.execute(u3);
                pool.execute(u4);
                //关闭线程池
                pool.shutdown();
        }
}

/**
* 信用卡的用户
*/
class User implements Runnable {
        private String name;                //用户名
        private MyCount myCount;        //所要操作的账户
        private int iocash;                 //操作的金额,当然有正负之分了
        private Lock myLock;                //执行操作所需的锁对象

        User(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;
        }

        @Override
        public String toString() {
                return "MyCount{" +
                                "oid='" + oid + '\'' +
                                ", cash=" + cash +
                                '}';
        }
}

张三正在操作MyCount{oid='95599200901215522', cash=10000}账户,金额为-4000,当前金额为10000
张三操作MyCount{oid='95599200901215522', cash=6000}账户成功,金额为-4000,当前金额为6000
张三他爹正在操作MyCount{oid='95599200901215522', cash=6000}账户,金额为6000,当前金额为6000
张三他爹操作MyCount{oid='95599200901215522', cash=12000}账户成功,金额为6000,当前金额为12000
张三他弟正在操作MyCount{oid='95599200901215522', cash=12000}账户,金额为-8000,当前金额为12000
张三他弟操作MyCount{oid='95599200901215522', cash=4000}账户成功,金额为-8000,当前金额为4000
张三正在操作MyCount{oid='95599200901215522', cash=4000}账户,金额为800,当前金额为4000
张三操作MyCount{oid='95599200901215522', cash=4800}账户成功,金额为800,当前金额为4800

Process finished with exit code 0

从上面的输出可以看到,利用锁对象太方便了,比直接在某个不知情的对象上用锁清晰多了。

但一定要注意的是,在获取了锁对象后,用完后应该尽快释放锁,以便别的等待该锁的线程有机会去执行。


在上文中提到了Lock接口以及对象,使用它,很优雅的控制了竞争资源的安全访问,但是这种锁不区分读写,称这种锁为普通锁。为了提高性能,Java提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制,在一定程度上提高了程序的执行效率。

Java中读写锁有个接口java.util.concurrent.locks.ReadWriteLock,也有具体的实现ReentrantReadWriteLock,详细的API可以查看JavaAPI文档。

下面这个例子是在文例子的基础上,将普通锁改为读写锁,并添加账户余额查询的功能,代码如下:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
* Java线程:锁
*
* @author leizhimin 2009-11-5 10:57:29
*/
public class Test {
        public static void main(String[] args) {
                //创建并发访问的账户
                MyCount myCount = new MyCount("95599200901215522", 10000);
                //创建一个锁对象
                ReadWriteLock lock = new ReentrantReadWriteLock(false);
                //创建一个线程池
                ExecutorService pool = Executors.newFixedThreadPool(2);
                //创建一些并发访问用户,一个信用卡,存的存,取的取,好热闹啊
                User u1 = new User("张三", myCount, -4000, lock, false);
                User u2 = new User("张三他爹", myCount, 6000, lock, false);
                User u3 = new User("张三他弟", myCount, -8000, lock, false);
                User u4 = new User("张三", myCount, 800, lock, false);
                User u5 = new User("张三他爹", myCount, 0, lock, true);
                //在线程池中执行各个用户的操作
                pool.execute(u1);
                pool.execute(u2);
                pool.execute(u3);
                pool.execute(u4);
                pool.execute(u5);
                //关闭线程池
                pool.shutdown();
        }
}

/**
* 信用卡的用户
*/
class User implements Runnable {
        private String name;                //用户名
        private MyCount myCount;        //所要操作的账户
        private int iocash;                 //操作的金额,当然有正负之分了
        private ReadWriteLock myLock;                //执行操作所需的锁对象
        private boolean ischeck;        //是否查询

        User(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;
        }

        @Override
        public String toString() {
                return "MyCount{" +
                                "oid='" + oid + '\'' +
                                ", cash=" + cash +
                                '}';
        }
}

写:张三正在操作MyCount{oid='95599200901215522', cash=10000}账户,金额为-4000,当前金额为10000
写:张三操作MyCount{oid='95599200901215522', cash=6000}账户成功,金额为-4000,当前金额为6000
写:张三他弟正在操作MyCount{oid='95599200901215522', cash=6000}账户,金额为-8000,当前金额为6000
写:张三他弟操作MyCount{oid='95599200901215522', cash=-2000}账户成功,金额为-8000,当前金额为-2000
写:张三正在操作MyCount{oid='95599200901215522', cash=-2000}账户,金额为800,当前金额为-2000
写:张三操作MyCount{oid='95599200901215522', cash=-1200}账户成功,金额为800,当前金额为-1200
读:张三他爹正在查询MyCount{oid='95599200901215522', cash=-1200}账户,当前金额为-1200
写:张三他爹正在操作MyCount{oid='95599200901215522', cash=-1200}账户,金额为6000,当前金额为-1200
写:张三他爹操作MyCount{oid='95599200901215522', cash=4800}账户成功,金额为6000,当前金额为4800

Process finished with exit code 0

在实际开发中,最好在能用读写锁的情况下使用读写锁,而不要用普通锁,以求更好的性能。
分享到:
评论

相关推荐

    Java多线程编程总结

    Java线程:线程的同步与锁 Java线程:线程的交互 Java线程:线程的调度-休眠 Java线程:线程的调度-优先级 Java线程:线程的调度-让步 Java线程:线程的调度-合并 Java线程:线程的调度-守护线程 Java线程:线程的...

    java线程详解

    Java线程:线程的同步与锁 一、同步问题提出 二、同步和锁定 三、静态方法同步 四、如果线程不能不能获得锁会怎么样 五、何时需要同步 六、线程安全类 七、线程死锁 八、线程同步小结 Java线程:线程的交互...

    Java线程:线程状态的转换

    ### Java线程:线程状态的转换 #### 一、线程状态及其转换 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一个进程中的多个线程共享同一份内存空间,使得线程间的通信...

    Java多线程编程经验

    #### 六、Java线程:线程的同步与锁 为了确保线程安全,Java提供了多种机制来实现线程间的同步,包括但不限于: 1. **同步方法**:通过将方法声明为 `synchronized` 来实现。 2. **同步块**:使用 `synchronized` ...

    Java线程:线程状态的转换.pdf

    Java线程的状态主要包括以下五种: 1. 新状态(New):当通过`new Thread()`创建了一个线程对象,但还未调用`start()`方法时,线程处于新状态。此时,线程尚未启动,不具备执行的能力。 2. 可运行状态(Runnable)...

    Java 线程开发讲座

    #### 五、Java线程:线程的同步与锁 - **同步机制**:同步机制是为了防止多个线程同时访问同一资源而导致的数据不一致问题。 - **锁**:锁是一种常用的同步工具,分为内置锁(synchronized关键字)和显式锁...

    Java线程:线程状态的转换[参考].pdf

    Java线程的状态转换是理解多线程编程的关键概念。线程在生命周期中经历五个主要状态:新状态、可运行状态、运行状态、等待/阻塞状态以及死亡态。 1. **新状态**:当通过`new Thread()`创建一个新的线程对象,但尚未...

    java多线程编程总结

    #### 五、Java线程:线程的同步与锁 - **线程同步** 线程同步的主要目的是解决多线程环境下的资源共享问题,确保资源的安全访问。Java提供了多种同步机制,包括但不限于: 1. **同步方法**:通过在方法声明上...

    Java多线程

    #### 五、Java线程:线程的同步与锁 线程同步是确保多个线程正确访问共享资源的关键。Java提供了多种同步机制,包括同步方法、同步块和显式锁(`Lock`接口)。 #### 六、Java线程:线程的交互 线程之间的交互涉及到...

    Java_多线程编程线程:大总结

    - **Java线程的分类**:Java线程主要分为用户线程和守护线程。用户线程的结束会直接影响JVM的生命周期,而守护线程通常由系统或库管理,如垃圾回收线程。 #### 三、Java线程的创建与启动 Java提供了两种主要的方式...

    线程 JAVA java线程 java线程第3版 java线程第2版第3版合集

    电子书相关:包含4个有关JAVA线程的电子书(几乎涵盖全部有关线程的书籍) OReilly.Java.Threads.3rd.Edition.Sep.2004.eBook-DDU Java Thread Programming (Sams) java线程第二版中英文 java线程第二版中英文 ...

    java多线程、锁的教程跟案例

    Java多线程与锁是Java并发编程中的核心概念,它们对于构建高效、可扩展的并发应用程序至关重要。在Java中,多线程允许程序同时执行多个任务,提高CPU的利用率,而锁则是用来控制多线程间共享资源的访问,确保数据的...

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

    4种常用Java线程锁的特点,性能比较、使用场景 线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发...

    JAVA多线程总结

    **五、Java线程:线程的同步与锁** - **同步**:防止多个线程同时访问共享资源,如`synchronized`关键字用于同步方法或同步块。 - **锁**:包括内置锁(监视器锁)和显式锁(如ReentrantLock),用于控制对共享资源...

    java多线程之并发锁

    Java 多线程之并发锁 Java 中的多线程编程是指在一个程序中同时运行多个线程,以提高程序的执行效率和响应速度。在多线程编程中,线程间的同步是非常重要的,因为不同的线程可能会同时访问同一个共享资源,导致数据...

    JAVA多线程

    #### Java线程:线程的同步与锁 在多线程环境中,为防止多个线程同时访问共享资源导致的数据不一致,需要进行线程同步。Java提供了多种同步机制,包括synchronized关键字、Lock接口及其实现类(如ReentrantLock)以及...

    java线程.pdf

    根据提供的信息,我们可以推断出这份文档主要关注的是Java线程的相关内容。下面将围绕“Java线程”这一主题展开详细的介绍与解释。 ### Java线程基础 在Java语言中,线程是程序执行流的基本单元。一个标准的Java...

    java线程进程锁资源

    Java线程和进程是计算机程序执行的两种基本方式,它们在多任务环境下尤为关键。Java作为一门多线程编程语言,提供了丰富的工具和机制来管理线程和进程,以实现高效的并发执行。 **进程与线程的区别** 1. **进程**:...

    Java多线程运算集合

    #### 十二、Java线程:新特征-锁(上)与锁(下) - **锁的新特性**: - Java 5引入了新的锁机制,如 `ReentrantLock`、`ReadWriteLock` 等。 - 这些锁机制提供了比传统的 `synchronized` 更加灵活和强大的功能。...

Global site tag (gtag.js) - Google Analytics