重入锁:java.util.concurrent.locks.ReentrantLock

特点:

⑴显式加锁,解锁

⑵可以多次加锁,重入锁,但是,解锁次数必须与加锁次数相同;

package com.dong.testThread;
/**
 * 重入锁的使用
 * 
 * author:liuD
 */
import java.util.concurrent.locks.ReentrantLock;

public class TestReenterLock implements Runnable{
	public static ReentrantLock lock = new ReentrantLock();
	public static int i = 0;
	public static int j = 0;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int k = 0 ;k < 1000 ; k++) {
			//重入锁的特点,锁可以定义多个;
			lock.lock();
			lock.lock();
			try{
				//System.out.println(i);
				i++;
				j++;
			}finally {
				lock.unlock();
				lock.unlock();
			}
		}
	}
	public static void main(String[] args) throws InterruptedException {
		TestReenterLock tr = new TestReenterLock();
		Thread t1 = new Thread(tr);
		Thread t2 = new Thread(tr);
		t1.start();
		t2.start();
		//停止当前主线程,让t1线程运行
		t1.join();
		t2.join();
		System.out.println("i = " + i + " j = " + j);

	}
	
}

⑶中断相应:线程可以被中断,即在等待锁的过程中,可以根据需要取消对锁的请求

lockInterruptibly ()方法,可以对中断进行相应的锁申请动作,即在等待锁的过程中,可以响应中断;

package com.dong.testThread;

import java.util.concurrent.locks.ReentrantLock;
/**
 * lock.lockInterruptibly()方法,对锁的请求,统一(注意是统一,即只要是同一个锁,在不同的方法中,都可以对锁的请求进行中断)使用lockInterruptibly()方法,可以中断进行响应的锁申请动作,
 * 即等地锁的过程中,可以相应中断
 *
 *程序分析:t1使用资源,设置一个标记,用来表示锁资源正在使用(也可以将共享资源设置为一个方法,例如读方法,和写方法,当在写的过程中,如果有线程想执行读方法,
 * 则使用中断,让其中断对锁的请求),当t2发现t1正在使用资源时,边会放弃请求锁对象,t1继续执行; 注意:这个程序在百分之90的情况下都是按照咱们的逻辑
 * 运行,但是有一种情况,不会输出t1的执行过程,为什么?因为当线程启动时,随机执行,t1执行完syso后,然后开始执行lock1.lock()方法,这个时候准
 * 备获取锁,如果刚好t2中lock1.lockInterruptibly执行,则会释放t1对象的锁;只要t1加锁成功,则t2的lock1.lockInterruptibly()
 * 方法将会获取锁,对于正在使用的锁,t2线程会放弃请求锁对象,则t1正常输出;主要还是,这两个对象使用同一个重入锁;
 * 
 * @author liuD
 *
 */
public class testLockInterruptibly {
	
	public static ReentrantLock lock1 = new ReentrantLock();
	public static volatile boolean status = true;
	public static void main(String[] args) {
		Thread t1  = new Thread(new Resource());
		Thread t2 = new Thread(new Customer());
		
		t1.start();
		t2.start();
	}
static class Resource implements Runnable{
	@Override
	public void run() {
			System.out.println(Thread.currentThread().getName()+"已经开始使用设备");
			lock1.lock();
			try {
				for(int i = 0;i<1000;i++) {
					System.out.println(Thread.currentThread().getName()+"设备使用中..........................."+i);
				}
			}finally {
				
				lock1.unlock();
			}	
	}
}
static class Customer implements Runnable{
	
	public void run() {
		System.out.println(Thread.currentThread().getName()+"请求使用设备。。。。。");
	
		if(testLockInterruptibly.status == true) {
			try {
				System.out.println("好的,"+Thread.currentThread().getName()+"去做别的去了");
				//取消对锁的请求,
				lock1.lockInterruptibly();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
}

⑷tryLock()方法: 接受两个参数,一个表示等待时间,另一个表示计时单位,只需将往常锁,替换成tryLock()即可;

⑸公平锁:

在大多说情况下,锁的竞争都是不公平的,根据线程的优先级来进行竞争,有可能造成饥饿现象,同时使用wait()方法或者因为多线程情况下,造成的阻塞状况,在对其唤醒的时候,都是从阻塞队列中随机唤醒一个线程,也有可能造成不平共的现象。公平锁则是会按照先后顺序,保证先到的先得,公平锁也可保证不会产生饥饿现象,只要你排队,最终都可以等到资源的,使用Synchronized关键字进行锁控制,产生的锁就是非公平的,我们需要使用重入锁的另一个构造函数: public ReentrantLock(boolean fair);fair为true时,表示锁是公平的,因为公平锁需要维护一个有序队列,所以公平锁的实现成本较高,性能相对低下,默认情况下也是非公平的,如果没有特殊的要求,也不需要使用公平锁.

⑹Condition条件:

 在synchronzied关键字中,wait()和notify()方法可让线程等待和唤醒,在ReentrantLock中也有与其搭配的线程等待和唤醒搭档,即Condition实例,利用Condition对象,我们可以让线程在何时的时间等待,或者在某一个特定的时刻得到通知,继续执行;Condition 接口有以下方法:await()方法会是当前线程等待,同时释放当前锁,但其他线程使用signal()或者signalAll()方法是,线程会重新获得 锁并继续执行,(与notfiy()和notifyAll()方法同理)awaitUniterruptibly()方法与await()方法基本相同,但是他不会在等待过程中相应中断;singal()方法用于唤醒一个在等待中的线程。

package com.dong.testThread;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
 * Condition条件:
 *
 * @author liuD
 */
public class TestCondition implements Runnable{
	public static ReentrantLock lock = new ReentrantLock();
	//使用lock.newCondition()可以生成一个与当前重入锁绑定的Condition实例;
	public static Condition condition = lock.newCondition();
	
	public static void main(String[] args) throws InterruptedException {
		TestCondition to = new TestCondition();
		Thread t1 = new Thread(to);
		t1.start();
		//主线程睡眠2秒
		Thread.sleep(2000);
		
		lock.lock();
		System.out.println(Thread.currentThread().getName() + " 唤醒进入等待的线程");
		////唤醒线程,condition.signal()调用后,这个线程会释放锁,同时也会要求先获得相关的锁(即显式的lock()),在signal()方法后,系统会从当前condition对象的
		//等待队列中,唤醒一个线程
		condition.signal();
		lock.unlock();
		
	}
	
	@Override
	public void run() {
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName()+ " 线程进入等待队列 ");
			//使当前线程进入Condition对象的等待队列中;
			condition.await();
			System.out.println(Thread.currentThread().getName()+ "  is going on");
		} catch (Exception e) {
			// TODO: handle exception
		}finally {
			
		}
	}
	
}

 ⑺读写锁 ReadWriteLock

JDK5中提供的读写分离锁,使得读和写操作的有序性,同时保证数据的一致性,在多线程中,读写锁的约束为:当线程在进行读时,另一个线程也可以进行读,并不会让其阻塞,当一个线程在共享资源中执行写操作时,另一个线程如果想访问该共享资源,会使其阻塞,直到线程的锁释放才可以,只要是涉及写操作,都会造成阻塞;当读操作次数远远大于写操作,则读写锁就可以发挥最大的功效,提升系统的性能。

读写锁的代码比较多,专门作为一篇笔记进行整理

 

内容参考《高并发程序设计》 葛一鸣 郭超 ,,由衷感谢此书作者为我们提供学习内容