1.1线程重入锁技术及应用
1.1.1重入锁java.util.concurrent.locks.ReentrantLock
1、重入锁java.util.concurrent.locks.ReentrantLock
(1)Lock接口的实现类
java.util.concurrent.locks中提供了几个Lock接口的实现类,比较常用的是ReentrantLock 类。
(2)使用ReentrantLock能够产生出线程安全的代码
在Java 5之前,我们只能使用synchronized来锁定同步的代码。Java 5中引入了性能更佳、粒度更细的可重入锁java.util.concurrent.locks.ReentrantLock,从而保证代码具有更好的线程安全性。
ReentrantLock是一个可重入的互斥锁,它具有与使用synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更加强大——添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。
此外,它还提供了在激烈争用情况下更佳的性能——当许多线程都想访问共享资源时,JVM 可以花更少的时候来调度线程,把更多时间用在执行线程上。
(3)Condition接口
就像Lock 接口是同步的具体化,Condition 接口是Object 中wait() 和notify() 方法的具体化。Lock 中的一个方法是newCondition(),它要求锁定向该锁定返回新的Condition 对象限制。
await()、signal() 和signalAll() 方法类似于wait()、notify() 和notifyAll(),但增加了灵活性,每个Lock 都可以创建多个条件变量。这简化了一些并发算法的实现。
(4)java.util.concurrent.locks.ReadWriteLock提供了一对可供读写并发的锁ReentrantLock 实现的锁定规则非常简单——每当一个线程具有锁定时,其他线程必须等待,直到该锁定可用。但有时当对数据结构的读取通常多于修改时,可以使用更复杂的称为读写锁定的锁定结构,它允许有多个并发读者,同时还允许一个写入者独占锁定。
ReadWriteLock在一般情况下(只读)提供了更大的并发性,同时在必要时仍提供独占访
问的安全性。ReadWriteLock 接口和ReentrantReadWriteLock 类提供这种功能——多读者、单写入者锁定规则,可以用这种功能来保护共享的易变资源。
2、应用synchronized时所存在的问题
(1)存在的问题一
synchronized代码块在应用中具有原子性(atomicity),原子性意味着一个线程一次只能执行由一个指定监控对象(lock)保护的代码,从而防止多个线程在更新共享状态时相互冲突——synchronized是独占锁,独占锁是一种悲观锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。
(2)存在的问题二
另外也不区分共享同步(一般是并发的读操作)与互斥同步(一般是写操作),所有同步都只能是完全排他的,只要有并发写的可能性就不得不把全部读操作也互斥同步,从而丧失并发读取的可能性。
(3)存在的问题三
主要体现在灵活性方面的弊病, 比如Object.wait(); Object.notify()等方法必须在该对象的同步块内执行(否则会抛IllegalMonitorStateException),并且一个对象只能wait/notify一个状态。
而java.util.concurrent.locks包中的有关类则是通过让一个Lock可以建多个“条件”去wait/notify增强了灵活性,而且Lock能完成synchronized所实现的所有功能。
因此,synchronized代码块在“读远多过于写”的并发状况下存在严重的性能问题,以至于硬件新增长出来的并发能力在普通应用中将被大部分折扣掉。当然,应用synchronized比Lock要简单的多。
3、应用重入的互斥锁完善前面的示例程序的代码示例
(1)lock()和unlock()方法
1)void lock()方法是获取锁,如果锁不可用,出于线程调度目的,将禁用当前线程,并
且在获得锁之前,该线程将一直处于休眠状态。
2)void unlock()方法是释放锁,对应于lock()、tryLock()、tryLock(xx)、lockInterruptibly()
等操作,应用该方法可以避免死锁或者资源浪费。
lock()方法可以获取可重入的锁,而如果该锁没有被另一个线程保持,则获取该锁并立即
返回,将锁的保持计数设置为1;如果当前线程已经保持该锁,则将保持计数加1,并且该方法立即返回;如果该锁被另一个线程保持,则出于线程调度的目的,禁用当前线程,并且在获得锁之前,该线程将一直处于休眠状态,此时锁保持计数被设置为1。
(2)为什么Lock能够保证线程安全
是因为Lock对象的lock()方法保证了只有一个线程能够只有此锁。但需要说明的是对于任何一个lock()方法,都需要一个unlock()方法与之对于,通常情况下为了保证unlock方法总是能够得到执行,unlock方法要被置于finally块中。
(3)修改SynchronizedStack类后的代码示例
使用Lock时需要自己获得锁并在使用后手动释放,这一点与synchronized有所不同;另外,Lock必须要在finally 块中释放。否则,如果受保护的访问共享数据的程序代码段抛出异常后,同步锁就有可能永远得不到释放!
由于synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally 从句中释放。
(4)代码示例
package com.px1987.threaddemo;
import java.util.LinkedList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SynchronizedStack {
private Lock someOneReentrantLock = new ReentrantLock();
private LinkedList
/**
*保存数据的方法
*/
public void pushSomeOneData(Object someOneData) {
System.out.println("开始压栈:" + someOneData);
someOneReentrantLock.lock();
try{
allDataList.addLast(someOneData);
}
finally{
someOneReentrantLock.unlock();
}
System.out.println("结束压栈:" + someOneData);
}
/**
*恢复数据的方法
*/
public Object popSomeOneData() throws Exception { System.out.println("开始弹栈");
while (allDataList.size() <= 0) {
}
Object returnObject;
someOneReentrantLock.lock();
try{
returnObject=allDataList.removeLast();
}
finally{
someOneReentrantLock.unlock();
}
return returnObject;
}
}
(5)程序执行的结果
4、正确地释放同步锁
(1)下面的SynchronizedStack类的代码将是不可靠的程序代码
尽管也能够满足功能的需要,但如果在操作共享数据数据时出现了异常,将不能正常地释放同步锁。异常,为了保证可靠地释放同步锁,必须要将释放的代码放入finally语句块中。package com.px1987.threaddemo;
import java.util.LinkedList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SynchronizedStack {
private Lock someOneReentrantLock = new ReentrantLock();
private LinkedList
/**
*保存数据的方法
*/
public void pushSomeOneData(Object someOneData) {
System.out.println("开始压栈:" + someOneData);
someOneReentrantLock.lock();
allDataList.addLast(someOneData);
someOneReentrantLock.unlock();
System.out.println("结束压栈:" + someOneData);
}
/**
*恢复数据的方法
*/
public Object popSomeOneData() throws Exception { System.out.println("开始弹栈");
while (allDataList.size() <= 0) {
}
Object returnObject;
someOneReentrantLock.lock();
returnObject=allDataList.removeLast();
someOneReentrantLock.unlock();
return returnObject;
}
}
(2)正确和可靠地释放同步锁的代码示例
package com.px1987.concurrent;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class AtomicIntegerWithLock {
private int value;
private Lock lock = new ReentrantLock();
public AtomicIntegerWithLock() {
super();
}
public AtomicIntegerWithLock(int value) { this.value = value;
}
public final int get() {
lock.lock();
try {
return value;
} finally {
lock.unlock();
}
}
public final void set(int newValue) {
lock.lock();
try {
value = newValue;
} finally {
lock.unlock();
}
}
public final int getAndSet(int newValue) { lock.lock();
try {
int ret = value;
value = newValue;
return ret;
} finally {
lock.unlock();
}
public final boolean compareAndSet(int expect, int update) { lock.lock();
try {
if (value == expect) {
value = update;
return true;
}
return false;
} finally {
lock.unlock();
}
}
public final int getAndIncrement() {
lock.lock();
try {
return value++;
} finally {
lock.unlock();
}
}
public final int getAndDecrement() {
lock.lock();
try {
return value--;
} finally {
lock.unlock();
}
public final int incrementAndGet() { lock.lock();
try {
return ++value;
} finally {
lock.unlock();
}
}
public final int decrementAndGet() { lock.lock();
try {
return --value;
} finally {
lock.unlock();
}
}
public String toString() {
return Integer.toString(get());
}
}
简答题 1、传统的结构化程序设计的优点和缺点是什么? 优点: (1)这种程序设计方法力求算法描述准确。 (2)对每一子过程模块容易进行程序正确性证明。 缺点: (1)这种程序设计方法本质上是面向“过程”的,而“过程”和“操作”又是不稳定和多变的,因此不能直接反映人类求解问题的思路。 (2)程序代码可重用性差。程序中除少数标准库函数外,每设计一个程序时,程序员几乎从零做起。即使重用代码,通常也是通过拷贝或编辑重新生成一份。 (3)维护程序的一致性困难。该种方法将数据与对数据进行处理的程序代码分离。 2、什么是对象? 每个对象都具有属性(Attribute)和方法(Method)这两方面的特征。对象的属性描述了对象的状态和特征,对象的方法说明了对象的行为和功能,并且对象的属性值只应由这个对象的方法来读取和修改,两者结合在一起就构成了对象的完整描述。 3、什么是消息? 在面向对象的程序设计中,由于对象描述了客观实体,它们之间的联系通过对象间的联系来反映。当一个对象需要另外一个对象提供服务时,它向对方发出一个服务请求,而收到请求的对象会响应这个请求并完成指定的服务。这种向对象发出的服务请求就称为消息。4、什么是多态性? 所谓多态性是指当程序中的其他部分发出同样的消息时,按照接收消息对象的不同能够自动执行类中相应的方法。其好处是,用户不必知道某个对象所属的类就可以执行多态行为,从而为程序设计带来更大方便。 5、什么是面向对象的程序设计方法? 这种方法将设计目标从模拟现实世界的行为转向了模拟现实世界中存在的对象及其各自的行为。 在OOP中,将“对象”作为系统中最基本的运行实体,整个程序即由各种不同类型的对象组成,各对象既是一个独立的实体,又可通过消息相互作用,对象中的方法决定要向哪个对象发消息、发什么消息以及收到消息时如何进行处理等。 6、面向对象方法的特点是什么? (1)OOP以“对象”或“数据”为中心。由于对象自然地反映了应用领域的模块性,因此具有相对稳定性,可以被用作一个组件去构成更复杂的应用,又由于对象一般封装的是某一实际需求的各种成分,因此,某一对象的改变对整个系统几乎没有影响。 (2)引入了“类”(class)的概念。类与类以层次结构组织,属于某个类的对象除具有该类所描述的特性外,还具有层次结构中该类上层所有类描述的全部性质,OOP中称这种机制为继承。 (3)OOP方法的模块性与继承性,保证了新的应用程序设计可在原有对象的数据类型和功能的基础上通过重用、扩展和细化来进行,而不必从头做起或复制原有代码,这样,大大减少了重新编写新代码的工作量,同时降低了程序设计过程中出错的可能性,达到了事半功倍的效果。 7、面向对象的程序设计方法与结构化程序设计方法的比较 (1)传统的结构化程序设计方法以过程为中心构造应用程序,数据和处理数据的过程代码是分离的、相互独立的实体,设计出的程序可重用代码少,且当代码量增加时维护数据和代码的一致性困难。
《面向对象程序设计》 大作业 题目学生成绩管理系统 学院 专业 班级 姓名 指导教师 2015 年11 月11 日
目录 一大作业的目的 (1) 二大作业的内容............................ . .. (2) 三大作业的要求与数据............... ...... . (3) 四大作业应完成的工作.................. . (4) 五总体设计(包含几大功能模块)........... . (5) 六详细设计(各功能模块的具体实现算法——流程图) (6) 七调试分析(包含各模块的测试用例,及测试结果) (7) 八总结 (8) 十参考资料 (9)
一大作业的目的 《面向对象程序设计》是一门实践性很强的课程,通过大作业不仅可以全方位检验学生知识掌握程度和综合能力,而且还可以进一步加深、巩固所学课程的基本理论知识,理论联系实际,进一步培养自己综合分析问题和解决问题的能力。更好地掌握运用C++语言独立地编写、调试应用程序和进行其它相关设计的技能。 二大作业的内容 对学生信息(包括学号、语文、数学、英语、平均分)进行管理,包括学生成绩的信息输入、输出、查询、删除、排序、统计、退出.将学生的成绩信息进行记录,信息内容包含:(1)学生的学号(2)学生的姓名(3)学生的成绩。假设,现收集到了一个班学生的所有成绩信息,要求用C语言编写一个简单的成绩管理系统,可进行录入、查询、修改和浏览等功能。学习相关开发工具和应用软件,熟悉系统建设过程。 三大作业的要求与数据 1、用C++语言实现系统; 2、对学生信息(包括学号、姓名、语文、数学、英语、平均分)进行管理,包括学生成绩的信息输入、输出、查询、删除、排序、统计、退出. 3、学生信息包括:其内容较多,为了简化讨论,要求设计的管理系统能够完成以下功能: (1) 每一条记录包括一个学生的学号、姓名、3门课成绩 (2)、成绩信息录入功能:(成绩信息用文件保存,可以一次完成若干条记录 的输入。) (3)、成绩信息显示浏览功能:完成全部学生记录的显示。 (4)、查询功能:完成按姓名查找学生记录,并显示。 (5)成绩信息的删除:按学号进行删除某学生的成绩. (6)、排序功能:按学生平均成绩进行排序。 (7)、应提供一个界面来调用各个功能,调用界面和各个功能的操作界面应尽可能清晰美观!