java多线程-lock的使用

        Lock就是使用ReentrantLock类,是在jdk1.5添加的类。相对比synchronized关键字有更加强大的功能,比如多路分支通知、读写锁控等等,更加灵活。

    1.1最简单的lock使用

public class LockTest {
			private Lock lock = new ReentrantLock();
			public void lockTest() {
				lock.lock();
				for (int i = 0; i < 10; i++) {
					System.out.println("ThreadName=" + Thread.currentThread().getName() + i);
				}
				lock.unlock();
			}
		}

        使用lock()方法获取锁,使用unlock()方法释放锁。所以被两个方法夹在中间的代码就是同步的。

1.2 Condition

        关键字sunchronized是通过wait() notify() notifyAll()方法相结合实现等待/通知模型。而ReentrantLock也可以实现同样的功能,但是需要借助于Condition对象。相对比synchronized,condition更灵活!因为可以创建多个condition实例。
使用notify/notidyAll是随机通知的,但是condition可以选择性通知。

注意:在调用await方法之前,必须用lock()方法获得同步监视器,不然会报错!

        代码如下:

                //服务类:
		public class MyService {
			Lock lock = new ReentrantLock();
			private Condition condition = lock.newCondition();
			public void await(){
				try{
					lock.lock();
					System.out.println("awair时间为"+System.currentTimeMillis());
					condition.await();
				}catch (Exception e){
					e.printStackTrace();
				}finally {
					lock.unlock();
					System.out.println("锁被释放了");
				}
			}

			public void signal(){
				try{
					lock.lock();
					System.out.println("signal时间为" + System.currentTimeMillis());
                    condition.signal();
				}catch (Exception e){
					e.printStackTrace();
				}finally{
					lock.unlock();
				}
			}
		}
                //线程A类:
		public class ThreadA implements  Runnable{
			private MyService myService;

			public ThreadA(MyService myService) {
				this.myService = myService;
			}

			@Override
			public void run() {
				myService.await();
			}
		}
		//Run运行类:
		public class Run {
			public static void main(String[] args) {
				try {
					MyService myService = new MyService();
					ThreadA a = new ThreadA(myService);
					Thread threadA = new Thread(a);
					threadA.start();
					Thread.sleep(1000);
					myService.signal();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		运行结果:
		awair时间为1526374729023
		signal时间为1526374730009

		Object类的wait()相当于Condition类的await()
		Object类的notify()相当于Condition类的notify()
		Object类的notifyAll()相当于Condition类的notifyAll()

1.2.1 实现多个condition的使用

        就是创建多个condition,并给每一个都写一个对应的await()和signal()。

        代码如下

public class MyService {
					Lock lock = new ReentrantLock();
					private Condition conditionA = lock.newCondition();
					private Condition conditionB = lock.newCondition();
					public void awaitA(){
						try{
							lock.lock();
							System.out.println("当前线程为"+Thread.currentThread().getName()+"。awaitA时间为"+System.currentTimeMillis());
							conditionA.await();
						}catch (Exception e){
							e.printStackTrace();
						}finally {
							lock.unlock();
						}
					}

					public void signalA(){
						try{
							lock.lock();
							System.out.println("当前线程为"+Thread.currentThread().getName()+"。signalA时间为" + System.currentTimeMillis());
							conditionA.signal();
						}catch (Exception e){
							e.printStackTrace();
						}finally{
							lock.unlock();
						}
					}

					public void signalAll_A(){
						try{
							lock.lock();
							System.out.println("当前线程为"+Thread.currentThread().getName()+"。signalAll_A时间为" + System.currentTimeMillis());
							conditionA.signalAll();
						}catch (Exception e){
							e.printStackTrace();
						}finally{
							lock.unlock();
						}
					}

					public void awaitB(){
						try{
							lock.lock();
							System.out.println("当前线程为"+Thread.currentThread().getName()+"。awaitB时间为"+System.currentTimeMillis());
							conditionB.await();
						}catch (Exception e){
							e.printStackTrace();
						}finally {
							lock.unlock();
						}
					}

					public void signalB(){
						try{
							lock.lock();
							System.out.println("当前线程为"+Thread.currentThread().getName()+"。signalB时间为" + System.currentTimeMillis());
							conditionB.signal();
						}catch (Exception e){
							e.printStackTrace();
						}finally{
							lock.unlock();
						}
					}

					public void signalAll_B(){
						try{
							lock.lock();
							System.out.println("当前线程为"+Thread.currentThread().getName()+"。signalAll_B时间为" + System.currentTimeMillis());
							conditionB.signalAll();
						}catch (Exception e){
							e.printStackTrace();
						}finally{
							lock.unlock();
						}
					}
				}
				Run类
				public class Run {
					public static void main(String[] args) {
						try {
							MyService myService = new MyService();
							ThreadA a = new ThreadA(myService,"A");
							ThreadA b = new ThreadA(myService,"B");
							Thread threadA = new Thread(a);
							Thread threadB = new Thread(b);
							threadA.start();
							threadB.start();
							Thread.sleep(1000);
							myService.signalA();
							myService.signalB();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}

					}
				}
				输出结果:
				当前线程为Thread-0。awaitA时间为1526376205793
				当前线程为Thread-1。awaitA时间为1526376205795
				当前线程为main。signalA时间为1526376206779
				当前线程为main。signalB时间为1526376206779

1.2.2 实现生产者/消费者模式:一对一交替打印

        即生产者生产一个商品,消费者消费一个商品。,通过同一个condition的await()和signal()控制。

        代码如下:

//Myservice:
			public class MyService {
				private Lock lock = new ReentrantLock();
				private Condition condition = lock.newCondition();
				private boolean hasValue = false;
				public void set(){
					try{
						lock.lock();
						while(hasValue){
							condition.await();
						}
						hasValue = true;
						System.out.println("生产");
						condition.signal();
					}catch (Exception e){
						e.printStackTrace();
					}finally {
						lock.unlock();
					}
				}

				public void get(){
					try{
						lock.lock();
						while(!hasValue){
							condition.await();
						}
						hasValue = false;
						System.out.println("消费");
						condition.signal();
					}catch (Exception e){
						e.printStackTrace();
					}finally {
						lock.unlock();
					}
				}
			}
			//ConsumerThread:
			public class ConsumerThread implements Runnable {
				private MyService myService;

				public ConsumerThread(MyService myService) {
					this.myService = myService;
				}

				@Override
				public void run() {
					for (int i = 0; i < Integer.MAX_VALUE ; i++) {
						myService.get();
					}
				}
			}
			//ProducerThread:
			public class ProducerThread implements  Runnable {
				private MyService myService;

				public ProducerThread(MyService myService) {
					this.myService = myService;
				}

				@Override
				public void run() {
					for (int i = 0; i < Integer.MAX_VALUE; i++) {
						myService.set();
					}
				}
			}
			//Run:
			public class Run {
				public static void main(String[] args) {
					try {
						MyService myService = new MyService();
						ConsumerThread a = new ConsumerThread(myService);
						ProducerThread b = new ProducerThread(myService);
						Thread threadA = new Thread(a);
						Thread threadB = new Thread(b);
						threadA.start();
						threadB.start();
					} catch (Exception e) {
						e.printStackTrace();
					}

				}
			}
			运行结果:生产
						消费
						生产
						消费
						生产
						消费
						......

        如果实现多对多的生产者消费者模式,其他都不需要改,只要多创建几个线程,然后把myservice的condition.signal();改成condition.signalAll();就可以防止出现假死状态!

  1.2.3 公平锁与非公平锁

        锁Lock分为公平锁和非公平锁
公平锁表示线程获取锁的顺序的按照线程的顺序来分配的,即先来先得的FIFO先进先出顺序。

非公平锁就是一种获取锁的抢占机制,是随机获得锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁!

    公平锁例子:

//Service:
	public class Service {
		private ReentrantLock lock;

		public Service(boolean isFair) {
			lock = new ReentrantLock(isFair);
		}
		public void serviceMethod(){
			try{
				lock.lock();
				System.out.println("ThreadName=" + Thread.currentThread().getName() + "获得锁定");
			}catch (Exception e){
				e.printStackTrace();
			}finally {
				lock.unlock();
			}
		}
	}

//RunFair:
	public class RunFair {
		public static void main(String[] args) {
			final Service service = new Service(true);
			Runnable runnable = new Runnable() {
				@Override
				public void run() {
					System.out.println("线程"+Thread.currentThread().getName() + "运行了");
					service.serviceMethod();
				}
			};
			Thread[] threadArray= new Thread[10];
			for (int i = 0; i < 10; i++) {
				threadArray[i] = new Thread(runnable);
			}
			for (int i = 0; i < 10; i++) {
				threadArray[i].start();
			}
		}
	}

运行后会发现打印结果基本呈有序的状态,这就是公平锁的特点
将final Service service = new Service(true);改成final Service service = new Service(false);就是非公平锁!

运行后会发现打印结果明显比公平锁乱!说明先start()启动的线程不代表先获得锁。

1.2.4 方法解说

    .getHoldCount():查询当前线程保持此锁定的个数,也就是调用lock()方法的次数。

    例子:

//service:
public class Service {
	private ReentrantLock lock = new ReentrantLock();
	public void serrviceMethod1(){
		try{
			lock.lock();
			System.out.println("serviceMrthod1 getHoldCount=" + lock.getHoldCount());
			serrviceMethod2();
		}catch (Exception e){
			e.printStackTrace();
		}finally {
			lock.unlock();
		}
	}
	public void serrviceMethod2(){
		try{
			lock.lock();
			System.out.println("serviceMrthod2 getHoldCount=" + lock.getHoldCount());
		}catch (Exception e){
			e.printStackTrace();
		}finally {
			lock.unlock();
		}
	}
}
//Run:
public class Run {
	public static void main(String[] args) {
		try {
			Service service = new Service();
			service.serrviceMethod1();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
}

.getQueueLength():返回正等待获取此锁定的线程估计数。

例子:

//Service:
public class Service {
	public  ReentrantLock lock = new ReentrantLock();
	public void serrviceMethod1(){
		try{
			lock.lock();
			System.out.println("ThreadName=" + Thread.currentThread().getName() + "进入方法!");
			Thread.sleep(Integer.MAX_VALUE);
		}catch (Exception e){
			e.printStackTrace();
		}finally {
			lock.unlock();
		}
	}
}
//Run:
public class Run {
	public static void main(String[] args) {
		final Service service = new Service();
		try {
		   Runnable runnable = new Runnable() {
			   @Override
			   public void run() {
				   service.serrviceMethod1();
			   }
		   };
			Thread[] threadArray = new Thread[10];
			for (int i = 0; i < 10; i++) {
				threadArray[i] = new Thread(runnable);
			}
			for (int i = 0; i < 10; i++) {
				threadArray[i].start();
			}
			Thread.sleep(2000);
			System.out.println("有线程数:" + service.lock.getQueueLength() + "在等待获取锁!");
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
}

.getWaitQueueLength():返回等待此锁定相关的给定条件condition的线程估计数。

例子:

	//Run:
	public class Run {
		public static void main(String[] args) {
			final Service service = new Service();
			try {
			   Runnable runnable = new Runnable() {
				   @Override
				   public void run() {
					   service.waitMethod();
				   }
			   };
				Thread[] threadArray = new Thread[10];
				for (int i = 0; i < 10; i++) {
					threadArray[i] = new Thread(runnable);
				}
				for (int i = 0; i < 10; i++) {
					threadArray[i].start();
				}
				Thread.sleep(2000);
				service.notidyMethod();
			} catch (Exception e) {
				e.printStackTrace();
			}

		}
	}
//Service:
	public class Service {
		public  ReentrantLock lock = new ReentrantLock();
		public Condition condition = lock.newCondition();
		public void waitMethod(){
			try{
				lock.lock();
				System.out.println("ThreadName=" + Thread.currentThread().getName() + "进入方法!");
				condition.await();
			}catch (Exception e){
				e.printStackTrace();
			}finally {
				lock.unlock();
			}
		}
		public void notidyMethod(){
			try{
				lock.lock();
				System.out.println("有"+lock.getWaitQueueLength(condition) + "个线程正在等待condition");
				condition.signalAll();
			}catch (Exception e){
				e.printStackTrace();
			}finally {
				lock.unlock();
			}
		}
	}

.boolean hasQueuedThread(Thread thread):查询指定线程是否正在等待获取此锁
.boolean hasQueuedThreads():查询是否有线程正在等待获取此锁定
.hasWaiters(Condition condition):查询是否有线程正在等待与此锁定有关的condition条件
.isFair():判断是否为公平锁
.boolean isHeldByCurrentThread():查询当前线程是否保持此锁定
.isLocked():查询此锁定是否由任意线程保持
.void lockInterruptibly():如果当钱线程未被中断,则获取锁定,如果已经被中断则出现异常。
.boolean tryLock():仅在调用时锁定未被另一个线程保持的情况下,才获取该锁定。
通俗来说,就是如果此时的lock没有被任何线程获取,我们调用lock.tryLock()返回true,并且,获取了lock的锁定!即lock.lock();如果锁定没有被unlock().其他线程再调用这个方法时返回false!
.tryLock(long timeOut,TimeUnit unit):如果锁定在给定等待时间内没有被另外衣蛾线程保持,且当前线程未被中断,则获取该锁定。
.awaitUninterruptibly():wait()中 的线程被interrrupt唤醒会报异常。但是用awaitUninterruptibly()让线程进入等待再interrupted就不会抛异常!
.awaitUntil:用此方法让线程等待一定时间,可以被其他线程提前唤醒,

1.3 使用ReentranReadWriteLock类的使用(读写锁)

    ReentrantLock具有完全互斥排他效果,即同一时间只有一个线程再执行ReentrantLock.lock()方法后面的任务。虽然保证了线程的安全性,但是效率特别低。所以 在JDK中提供了一种读写锁ReentrantReadWriteLock类,使它加快效率。注意!要求是不操作实例变量的方法中,才可以使用读写锁来提升反码运行速度。
读写锁也有两个锁:一个是读操作的锁,也称共享锁:另一个是写操作的锁,也叫排他锁。
多个读锁之间不互斥,读写锁互斥,写写锁互斥。

在没有线程进行写入操作时,进行读取操作的多个Thread都可以获取读锁

         进行写入操作的Thread只有在获取写锁后才能进行写入操作,即多个Thread可以同时进行读取操作,同一时刻只允许一个Thread进行写操作。

1.3.1 读读共享

使用读锁可以让多个读线程同时进入同一个被lock()的代码

例子:

//service:
public class Service {
	public ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
	public void read(){
		try{
			try{
				lock.readLock().lock();
				System.out.println("获得读锁" + Thread.currentThread().getName() + " " + System.currentTimeMillis());
				Thread.sleep(10000);
			}catch (Exception e){
				e.printStackTrace();
			}finally {
				lock.readLock().unlock();
			}
		}catch (Exception e){
			e.printStackTrace();
		}
	}
}
//Run:
public class Run {
	public static void main(String[] args) {
		Service service = new Service();
		try {
			ThreadA a  = new ThreadA(service);
			ThreadB b = new ThreadB(service);

		  Thread threadA = new Thread(a);
		  Thread threadB = new Thread(b);
		  threadA.setName("A");
		  threadB.setName("B");
		  threadA.start();
		  threadB.start();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
}
//ThreadA:
public class ThreadA implements Runnable{
	private Service service;

	public ThreadA(Service service) {
		this.service = service;
	}

	@Override
	public void run() {
		service.read();
	}
}
//ThreadB:
public class ThreadB implements Runnable{
	private Service service;

	public ThreadB(Service service) {
		this.service = service;
	}

	@Override
	public void run() {
		service.read();
	}
}
运行结果:
获得读锁A 1526439206124
获得读锁B 1526439206125

结果显示,在线程A.sleep不释放锁的情况下,线程B任然可以进入被锁的代码,大大加快的效率

1.3.2 写写互斥

    直接把上面的例子的service改成下面这样就可以了:

    

public class Service {
	public ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
	public void read(){
		try{
			try{
				lock.writeLock().lock();
				System.out.println("获得写锁" + Thread.currentThread().getName() + " " + System.currentTimeMillis());
				Thread.sleep(1000*3);
			}catch (Exception e){
				e.printStackTrace();
			}finally {
			   lock.writeLock().unlock();
			}
		}catch (Exception e){
			e.printStackTrace();
		}
	}
}

输出结果为:
获得写锁B 1526439801097

获得写锁A 1526439804099

很明显的B线程执行完后,A线程才进入这块代码。所以写写互斥!

1.3.3 读写互斥

    两个线程a,b,a线程进行读操作,b线程进行写操作。因为互斥,所以一个线程会等待另外一个线程运行完毕后才执行自己的线程!

    例子:

//Service:
public class Service {
	public ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

	public void read(){
		try{
			try{
				lock.readLock().lock();
				System.out.println("获得读锁" + Thread.currentThread().getName() + " " + System.currentTimeMillis());
				Thread.sleep(1000*3);
			}catch (Exception e){
				e.printStackTrace();
			}finally {
			   lock.readLock().unlock();
			}
		}catch (Exception e){
			e.printStackTrace();
		}
	}

	public void write(){
		try{
			try{
				lock.writeLock().lock();
				System.out.println("获得写锁" + Thread.currentThread().getName() + " " + System.currentTimeMillis());
				Thread.sleep(1000*3);
			}catch (Exception e){
				e.printStackTrace();
			}finally {
				lock.writeLock().unlock();
			}
		}catch (Exception e){
			e.printStackTrace();
		}
	}
}
//Run:
public class Run {
	public static void main(String[] args) {
		Service service = new Service();
		try {
			ThreadA a  = new ThreadA(service);
			ThreadB b = new ThreadB(service);

		  Thread threadA = new Thread(a);
		  Thread threadB = new Thread(b);
		  threadA.setName("A");
		  threadB.setName("B");
		  threadA.start();
		  threadB.start();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
}
//ThreadA:
public class ThreadA implements Runnable{
	private Service service;

	public ThreadA(Service service) {
		this.service = service;
	}

	@Override
	public void run() {
		service.read();
	}
}
//ThradB:
public class ThreadB implements Runnable{
	private Service service;

	public ThreadB(Service service) {
		this.service = service;
	}

	@Override
	public void run() {
		service.write();
	}
}
运行结果:
	获得读锁A 1526440490027
	获得写锁B 1526440493027
结果显而易见,互相交替运行的!

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值