Java复习-并发编程之lock

news/2024/5/17 15:21:43 标签: Java, 并发, Lock, synchronized

并发编程中,我们常用synchronized关键字来实现上锁,synchronized是java中的一个关键字,属于Java语言本身的内置特性,但是其在实际使用中存在一定的缺陷。

      例如:一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:

1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;
2)线程执行发生异常,此时JVM会让线程自动释放锁。

如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,试想一下,这多么影响程序执行效率。因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock就可以办到。

      再比如,当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象,这种情况也会影响效率。而利用Lock则可以解决这类问题。

      另外,synchronized和lock相比,synchronizedJava语言的内置特性,而Lock是一个java接口,通过这个类来实现同步访问。还有一点极其重要的是synchronized是不需要手动释放锁的,而Lock是需要在用完之后手动释放锁的(常用方式是加try catch finally语句,在finally中释放锁)

1、Lock

接下来,看代码,显示JDK1.8中示例的Lock使用:

	Lock l = ...;
 	l.lock();
 	try {
   		// access the resource protected by this lock
 	} finally {
   		l.unlock();
 	}

Lock接口中的方法有:


lock(),与lockInterruptibly(),返回值是void。采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。

  lockInterruptibly()方法比较特殊,当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就使说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。

2、Lock的实现类R entrantlock

利用Rentrantlock来实现Lock的各个方法:

public class LockTest {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		TestData testData = new TestData() ;
		Thread t1 = new Thread(testData);
		Thread t2 = new Thread(testData);

		t1.start();
		t2.start();
	}

}

class TestData implements Runnable{
	public ArrayList<Integer> arrayList = new ArrayList<Integer>();
	Lock lock = new ReentrantLock();
	
	public void insert() throws InterruptedException {
		lock.lock();
		try {
			long start = System.currentTimeMillis();
            
            while(System.currentTimeMillis() - start <= 1) {
                System.out.println(Thread.currentThread().getName()+"正在进行写操作");
            }
            System.out.println(Thread.currentThread().getName()+"写操作完毕");
		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			System.out.println(Thread.currentThread().getName()+"释放了锁");
			lock.unlock();
		}
		
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			insert();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			System.out.println(Thread.currentThread().getName()+"被中断");
		}
	}
	
}

运行的结果如下:


线程0先获得锁,运行完毕之后释放锁,线程1再获得锁进行运行。而利用reyLock(),可以获得是否得到锁的信息。

	public void insert() throws InterruptedException {
		if(lock.tryLock()) {
			try {
				long start = System.currentTimeMillis();
	            
	            while(System.currentTimeMillis() - start <= 1) {
	                System.out.println(Thread.currentThread().getName()+"正在进行写操作");
	            }
	            System.out.println(Thread.currentThread().getName()+"写操作完毕");
			} catch (Exception e) {
				// TODO: handle exception
			} finally {
				System.out.println(Thread.currentThread().getName()+"释放了锁");
				lock.unlock();
			}
		} else {
			System.out.println(Thread.currentThread().getName()+"获取锁失败");
		}
	}
上面,我们只是加入了tryLock(),来判断是否能得到锁,当得到锁就开始运行,得不到则返回false,且不在申请了:

我们还可以使用lockInterruptibly()响应中断:

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		TestData testData = new TestData() ;
		Thread t1 = new Thread(testData);
		Thread t2 = new Thread(testData);

		t1.start();
		t2.start();
		
		try {
            Thread.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
		t1.interrupt();
		
	}

}

class TestData implements Runnable{
	public ArrayList<Integer> arrayList = new ArrayList<Integer>();
	Lock lock = new ReentrantLock();
	
	public void insert() throws InterruptedException {
		lock.lockInterruptibly();
		try {
			long start = System.currentTimeMillis();
            
            while(System.currentTimeMillis() - start <= 1) {
                System.out.println(Thread.currentThread().getName()+"正在进行写操作");
            }
            System.out.println(Thread.currentThread().getName()+"写操作完毕");
		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			System.out.println(Thread.currentThread().getName()+"释放了锁");
			lock.unlock();
		}
		
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			insert();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			System.out.println(Thread.currentThread().getName()+"被中断");
		}
	}
	
}

Lock接口及其实现类ReentrantLock的基本使用如上。

此外还有ReadWriteLock接口及其实现类ReentrantReadWriteLock,这个是为了实现读写锁,来满足读写的时候锁的。

class TestData implements Runnable{
	public ArrayList<Integer> arrayList = new ArrayList<Integer>();
	ReadWriteLock lock = new ReentrantReadWriteLock();
	
	public void insert() throws InterruptedException {
		lock.writeLock().lock();
		try {
			long start = System.currentTimeMillis();
            
            while(System.currentTimeMillis() - start <= 1) {
                System.out.println(Thread.currentThread().getName()+"正在进行写操作");
            }
            System.out.println(Thread.currentThread().getName()+"写操作完毕");
		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			System.out.println(Thread.currentThread().getName()+"释放了锁");
			lock.writeLock().unlock();
		}
		
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			insert();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			System.out.println(Thread.currentThread().getName()+"被中断");
		}
	}
	
}

上面就是加了写锁writeLock的实现示例,运行结果和正常上锁差不多,但是读锁readLock在应用时就可以解决A线程使用锁时B线程等待的问题,利用读锁,两线程可以同时使用,提高效率。


2、Locksynchronized的不同:

1)Lock是一个接口,而synchronizedJava中的关键字,synchronized是内置的语言实现;
2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

5)Lock可以提高多个线程进行读操作的效率。
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。


3、其他一些锁的概念:

1)可重入锁: synchronized和lock都是可重入锁。意思是如果线程A使用synchronized method1方法申请了锁,此时有需要使用synchronized method2方法,这时就不需要在去申请锁了(因为可能A已经持有了对象的锁、再申请会申请不到,进而一直阻塞)。

2)可中断锁:synchronized不是可中断锁,lock是可中断锁。lock接口的lockInterruptibly()的用法时已经体现了Lock的可中断性,意思是在等待锁时,若线程不想等了,可以中断,不申请了。

3)公平锁:公平锁即体现等待时间长的锁优先申请到,lock接口的实现类ReentrantLockReadWriteLock接口及其实现类ReentrantReadWriteLock,可以在new时加true来实现公平锁。

3)读写锁:读写锁则是ReadWriteLock接口及其实现类ReentrantReadWriteLock,实现读和读的时候不发生冲突。











http://www.niftyadmin.cn/n/1194011.html

相关文章

部分常用面试题目及应答评点

问题1&#xff1a;你为什么觉得自己能够在这个职位上取得成就&#xff1f; 分析 这是一个相当宽泛的问题&#xff0c;它给求职者提供了一个机会&#xff0c;可以让求职者表明自己的热情和挑战欲。对这个问题的回答将为面试人在判断求职者是否对这个职位有足够的动力和自信心方面…

高性能服务器的分布式设计

何为高性能服务器 服务器分类 目前的高性能服务器&#xff0c;大家耳熟能详的有很多&#xff0c;这里对通用的做了分类&#xff1a;http服务器&#xff1a;nginx, apachejava http容器&#xff1a;tomcat, jettyjava 服务器框架&#xff1a; jetty, mina 其中nginx/apache/tomc…

TreeMap和红黑树

TreeMap的实现是红黑树算法的实现&#xff0c;所以要了解TreeMap就必须对红黑树有一定的了解,其实这篇博文的名字叫做&#xff1a;根据红黑树的算法来分析TreeMap的实现&#xff0c;但是为了与Java提高篇系列博文保持一致还是叫做TreeMap比较好。通过这篇博文你可以获得如下知识…

“WEI Share” 分享Windows 7 Experience Index

众所周知在Windows 7 中我们可以通过右键“我的电脑”->“属性”查看系统性能综合得分。这个分数包含了Processor、Memory、Graphics、Gaming Graphics、Hard drive 五方面性能测评分数。CodePlex 中有一个叫做“WEI Share” 的项目&#xff0c;涉及WPF、WCF、Silverlight、…

字符串转整数备录

using sysytem; convert.toint32 转载于:https://www.cnblogs.com/softimagewht/p/3801123.html

Spring入门之IOC

Spring是JavaEE的必学的框架之一&#xff0c;之前只稍微接触过&#xff0c;并没有系统性的深入了解和学习&#xff0c;最近打算开始系统的学习Spring的。今天写写一下关于Spring的IOC。 Spring的两大特性即为IOC和AOP&#xff0c;一个叫控制反转一个叫面向切面的编程。关于IOC…

Java多线程老鸟养成记(二):线程的状态控制

为什么80%的码农都做不了架构师&#xff1f;>>> 0、概述 Java提供了一些API用于线程状态的控制&#xff0c;在上篇文章 Java多线程老鸟养成记&#xff08;一&#xff09;&#xff1a;线程的生命周期 中已经提到了大多数常用的重要API&#xff0c;下面是来自jdk1.6…

微信公众平台开发(97) 图文消息

关键字&#xff1a;微信公众平台 开发模式 图文消息作者&#xff1a;方倍工作室原文&#xff1a;http://www.cnblogs.com/txw1958/p/weixin-97-news.html 在这篇微信公众平台开发教程中&#xff0c;我们将介绍如何灵活的使用图文消息功能。我们将只介绍被动回复消息中的图文消…