您的位置:首页 > 编程语言 > Java开发

java并发编程之线程同步基础(二)使用锁实现同步

2016-12-19 16:06 495 查看

1:Lock 使用锁实现同步

java提供了同步代码块的另一种机制 它是一种比synchronized关键字更强大更灵活的机制 这个机制基于Lock接口及其实现类
支持更灵活的同步快代码结构 使用sychronized关键字时  只能在同一个synchronized块结构中获取和释放控制  Lock接口徐云实现更复杂的临界区结构

相比sychronized关键字 Lock结构提供了更多的功能 其中一个新功能是tryLock方法实现 这个方法视图获取锁  如果锁已经被其他线程获取 它将返回false 并且继续往下执行代码 使用sychronized关键字时 如果现在A试图执行一个同步代码块 而线程B已在执行这个同步代码块 则线程A就会被挂起 知道线程B运行完这个同步代码块  。
Lock接口允许分离读和写操作 运行多个读线程和一个写线程

接下来就实现使用Lock接口和它的实现类ReentrantLock类来创建一个临界区 
创建一个打印队列
public class PrintQueue {
//声明一个锁对象 并且初始化
private final Lock queueLock=new ReentrantLock();
public void printJob(){
          调用lock方法获取对锁对象的控制
queueLock.lock();
try{
Long duration=(long) (Math.random()*10000);
System.out.println(Thread.currentThread().getName()+":PrintQueue: Printing a Job during "+(duration/1000)+" seconds");
Thread.sleep(duration);
}catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}finally{
//t通过unlock方法释放对锁对象的控制
queueLock.unlock();
}
}

}

创建打印工作类对象
public class Job implements Runnable {

private PrintQueue printQueue;
public Job(PrintQueue printQueue){
this.printQueue=printQueue;
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.printf("%s :Going to ptint a document\n",Thread.currentThread().getName());
printQueue.printJob( );
System.out.printf("%s : The doucument has been printed\n",Thread.currentThread().getName());

}

}

创建主类
public class Main {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
PrintQueue printQueue=new PrintQueue();
//创建10个打印工作Job对象 并把它作为传入参数创建线程
Thread thread[]=new Thread[10];
for(int i=0;i<10;i++){
thread[i]=new Thread(new Job(printQueue),"Thread"+i);
}
for(int i=0;i<10;i++){
thread[i].start();
}
}

}

工作原理:

这个例子中主要是PrintQueue中的printJob方法 使用锁实现一个临界区 并且保证同一时间只有一个执行线程访问这个临界区 必须创建ReentrantLock对象 在这个临界区的开始 必须通过lock方法获取对锁的控制 当线程A访问这个方法时  如果没有其他线程获取对这个锁的控制 lock方法将让线程A获的锁 并且允许它立刻访问临界区代码 不然其他线程B正在执行这个锁保护的临界区代码 Lock方法将让现在A休眠到线程B执行完临界区的代码  在离开临界区的时候我们必须使用unlock方法来释放它持有的锁
以让其他线程来访问临界区

2:使用读写锁实现同步数据访问

锁机制最大的改进之一就是ReadWriteLock接口和他唯一实现类ReentrantReadWriteLock  这个类有两个锁 一个是读写锁 另外一个是操作锁   使用读操作锁时可以允许多个线程同时访问 但是使用写操作锁时只允许一个线程  在一个现在执行写操作时  其他线程不能够执行读操作

下面就实现如何使用ReadWriteLock接口控制对价格对象的访问

创建一个加个信息类
public class PricesInfo {
private double price1;
private double price2;
/**
* 声明读写锁 ReadWriteLock
*/
private ReadWriteLock lock;
public PricesInfo(){
price1=1.0;
price1=2.0;
lock=new ReentrantReadWriteLock();
}
public double getPrice1(){
//使用读锁来获取对这个属性的访问
lock.readLock().lock();
double value=price1;
lock.readLock().unlock();
return value;
}
public double getPrice2(){
lock.readLock().lock();
double value=price2;
lock.readLock().unlock();
return value;
}

//使用写锁来获取对这个属性的访问
public void setPrice1(double price1,double price2){
lock.writeLock().lock();
this.price1=price1;
this.price2=price2;
lock.writeLock().unlock();
}

}

创建读取类 Reader
public class Reader implements Runnable {

private PricesInfo pricesInfo;
public Reader(PricesInfo pricesInfo){
this.pricesInfo=pricesInfo;
}
@Override
public void run() {
// TODO Auto-generated method stub
//读取两个价格值 
for(int i=0;i<10;i++){
System.out.printf("%s: Price1: %f\n",Thread.currentThread().getName(),pricesInfo.getPrice1());
System.out.printf("%s: Price 2: %f\n",Thread.currentThread().getName(),pricesInfo.getPrice2());
}
}

}

创建写入类Writer 这个类将修改价格信息
public class Writer implements Runnable {

private PricesInfo pricesInfo;
public Writer(PricesInfo pricesInfo){
this.pricesInfo=pricesInfo;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<3;i++){
System.out.printf("Writer: Attempt to modify the prices.\n");
pricesInfo.setPrice1(Math.random()*10, Math.random()*8);
System.out.printf("Writer: Prices hava been modified.\n");
try{
Thread.sleep(2);
}catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}

}

创建主类
public class Main {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
PricesInfo pricesInfo=new PricesInfo();
Reader []reader=new Reader[5];
Thread threadsReader[]=new Thread[5];
//创建五个读取类Reader  并把每一个对象作为传入参数创建线程
for(int i=0;i<5;i++){
reader[i]=new Reader(pricesInfo);
threadsReader[i]=new Thread(reader[i]);
}
Writer writer=new Writer(pricesInfo);
Thread threadWriter=new Thread(writer);
for(int i=0;i<5;i++){
threadsReader[i].start();
}
threadWriter.start();
}

}

运行结果如下
Thread-3: Price1: 2.000000

Thread-4: Price1: 2.000000

Writer: Attempt to modify the prices.

Thread-1: Price1: 2.000000

Thread-1: Price 2: 3.420673

Thread-0: Price1: 2.000000

Thread-2: Price1: 2.000000

Thread-0: Price 2: 3.420673

Thread-1: Price1: 1.044452

Thread-1: Price 2: 3.420673

Writer: Prices hava been modified.

Thread-4: Price 2: 0.000000

Thread-3: Price 2: 0.000000

Thread-4: Price1: 1.044452

Thread-4: Price 2: 3.420673

Thread-1: Price1: 1.044452

Thread-0: Price1: 1.044452

Thread-2: Price 2: 3.420673

Thread-0: Price 2: 3.420673

Thread-1: Price 2: 3.420673

Thread-4: Price1: 1.044452

Thread-3: Price1: 1.044452

Thread-4: Price 2: 3.420673

Thread-1: Price1: 1.044452

Thread-0: Price1: 1.044452

Thread-2: Price1: 1.044452

Thread-0: Price 2: 3.420673

Thread-1: Price 2: 3.420673

Thread-1: Price1: 1.044452

Thread-1: Price 2: 3.420673

Thread-4: Price1: 1.044452

Thread-3: Price 2: 3.420673

Thread-4: Price 2: 3.420673

Thread-4: Price1: 1.044452

Thread-1: Price1: 1.044452

Thread-0: Price1: 1.044452

Thread-2: Price 2: 3.420673

Thread-2: Price1: 1.044452

Thread-2: Price 2: 3.420673

Thread-2: Price1: 1.044452

Thread-0: Price 2: 3.420673

Thread-1: Price 2: 3.420673

Thread-4: Price 2: 3.420673

Writer: Attempt to modify the prices.

Thread-3: Price1: 1.044452

Writer: Prices hava been modified.

Thread-4: Price1: 1.044452

Thread-1: Price1: 1.044452

Thread-0: Price1: 1.044452

Thread-2: Price 2: 3.420673

Thread-0: Price 2: 1.312381

Writer: Attempt to modify the prices.

Thread-1: Price 2: 1.312381

Thread-1: Price1: 9.619444

Thread-4: Price 2: 1.312381

Thread-3: Price 2: 1.312381

Thread-4: Price1: 9.619444

Thread-4: Price 2: 5.728189

Thread-1: Price 2: 5.728189

Writer: Prices hava been modified.

Thread-0: Price1: 7.373355

Thread-2: Price1: 7.373355

Thread-0: Price 2: 5.728189

Thread-1: Price1: 9.619444

Thread-1: Price 2: 5.728189

Thread-4: Price1: 9.619444

Thread-3: Price1: 9.619444

Thread-4: Price 2: 5.728189

Thread-4: Price1: 9.619444

Thread-1: Price1: 9.619444

Thread-0: Price1: 9.619444

Thread-2: Price 2: 5.728189

Thread-0: Price 2: 5.728189

Thread-1: Price 2: 5.728189

Thread-4: Price 2: 5.728189

Thread-4: Price1: 9.619444

Thread-3: Price 2: 5.728189

Thread-4: Price 2: 5.728189

Thread-0: Price1: 9.619444

Thread-0: Price 2: 5.728189

Thread-0: Price1: 9.619444

Thread-2: Price1: 9.619444

Thread-2: Price 2: 5.728189

Thread-2: Price1: 9.619444

Thread-2: Price 2: 5.728189

Thread-2: Price1: 9.619444

Thread-2: Price 2: 5.728189

Thread-2: Price1: 9.619444

Thread-2: Price 2: 5.728189

Thread-2: Price1: 9.619444

Thread-2: Price 2: 5.728189

Thread-0: Price 2: 5.728189

Thread-0: Price1: 9.619444

Thread-0: Price 2: 5.728189

Thread-3: Price1: 9.619444

Thread-3: Price 2: 5.728189

Thread-3: Price1: 9.619444

Thread-3: Price 2: 5.728189

Thread-3: Price1: 9.619444

Thread-3: Price 2: 5.728189

Thread-3: Price1: 9.619444

Thread-3: Price 2: 5.728189

Thread-3: Price1: 9.619444

Thread-3: Price 2: 5.728189

Thread-3: Price1: 9.619444

Thread-3: Price 2: 5.728189

从结果中就可以看出 ReentrantWriteLock类有两种锁:一种是读操作锁 另一种是写操作锁  读操作锁通过ReadWriteLock接口的ReadLock方法获取的  这个锁实现了lock接口 所以我们可以使用lock unlock trylock方法 写操作锁是通过ReadWriteLock接口的WriteLock方法获取  当你获取lock接口的读锁时  不可以进行修改操作 不然会引起数据不一致的错误



3:修改锁的公平性

ReentrantLock和ReentranReadWriteLock类的构造器 都含有一个布尔参数 它允许你控制着两个类的行为 默认值是false 它称为非公平模式 在非公平模式下 当有很多线程在等待锁时 锁将选择他们中的一个来访问临界区 这个选择是没有任何约束的 如果默认值是true  则称为公平模式  在公平模式下 当有很多线程在等待锁时  锁将选择他们中的一个来访问临界区 而且选择的是等待时机最长的 这两种模式只适用于lock和unlock方法 而Lock接口的tryLock方法没有将现场置于休眠
默认属性并不影响这个方法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息