1,使用ReentrantLock类
在java中,可以使用synchronized关键字可以实现线程之间同步互斥,在jdk1.5中新增了ReentrantLock类也能够达到同样的效果,并且在扩展功能上面也更加强大,比如具有嗅探锁定、多路分支通知等功能,而且在使用上也比synchronized更加灵活。
使用ReentrantLock来实现线程同步
示例:创建一个共享对象MyService,将这个对象也是使用lock来锁住,只有拿到这个对象的线程才能够来操作这个资源中的代码,记住,lock是不会自己释放锁的,因此在代码执行完成以后是需要手动释放锁的。
class MyService{
private Lock lock = new ReentrantLock();
public void testMethod(){
//获得锁
lock.lock();
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() +"==>"+ (i));
}
//释放锁
lock.unlock();
}
}
class MyThread extends Thread{
private MyService myService;
public MyThread(MyService myService){
this.myService = myService;
}
@Override
public void run() {
myService.testMethod();
}
}
public class Test1 {
public static void main(String[] args) {
MyService myService = new MyService();
new MyThread(myService).start();
new MyThread(myService).start();
new MyThread(myService).start();
new MyThread(myService).start();
new MyThread(myService).start();
}
}
2,使用Condition实现等待/通知
当一个线程获取到锁了以后就可以执行同步代码块中的代码了,当这个线程执行完成以后会释放锁,这个时候主线程会休眠3秒钟,然后也会来执行同步代码块中的方法。
Object类 | Condition类 |
---|---|
wait() | await() |
wait(long timeout) | await(long timeout,TimeUnit unit) |
notify() | signal() |
notifyAll() | signalAll() |
class MyService1{
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void await(){
try{
lock.lock();
System.out.println("await时间为 " + System.currentTimeMillis());
condition.await();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void signal(){
try {
lock.lock();
System.out.println("signal时间为 " + System.currentTimeMillis());
condition.signal();
} finally {
lock.unlock();
}
}
}
class ThreadA extends Thread{
private MyService1 myService1;
public ThreadA(MyService1 myService1){
this.myService1 = myService1;
}
@Override
public void run() {
myService1.await();
}
}
public class Test2 {
public static void main(String[] args) throws InterruptedException {
MyService1 myService1 = new MyService1();
new ThreadA(myService1).start();
Thread.sleep(3000);
myService1.signal();
}
}
3,生产者/消费者模式
实现一对一交替打印
class MyService3{
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private boolean hasValue = false;
public void set(){
try {
lock.lock();
while (hasValue){
condition.await();
}
System.out.println("现在正在设置值");
hasValue = true;
condition.signal();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void get(){
try {
lock.lock();
while (!hasValue){
condition.await();
}
System.out.println("现在正在获取值");
hasValue = false;
condition.signal();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
class MyThreadA extends Thread{
private MyService3 myService3;
public MyThreadA(MyService3 myService3){
this.myService3 = myService3;
}
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
myService3.set();
}
}
}
class MyThreadB extends Thread{
private MyService3 myService3;
public MyThreadB(MyService3 myService3){
this.myService3 = myService3;
}
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
myService3.get();
}
}
}
public class Test3 {
public static void main(String[] args) {
MyService3 myService3 = new MyService3();
new MyThreadA(myService3).start();
new MyThreadB(myService3).start();
}
}
4,公平锁与非公平锁
公平与非公平锁:锁Lock分为“公平锁”和“非公平锁”,公平锁表示线程获取锁的顺序按照线程加锁的顺序来分配的(即先来先得的FIFO先进先出顺序),而非公平锁就是一种锁的抢占机制,是随机获得锁的,这种方式可能造成某些线程一直拿不到锁。
公平锁的实现:
class MyService4{
private ReentrantLock lock;
public MyService4(boolean isFair){
lock = new ReentrantLock(isFair);
}
public void serviceMethod(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "获得到锁");
}finally {
lock.unlock();
}
}
}
public class Test4 {
public static void main(String[] args) {
final MyService4 service4 = new MyService4(true);
Runnable runnable = () -> {
System.out.println(Thread.currentThread().getName()+"运行了");
service4.serviceMethod();
};
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(runnable);
}
for (int i = 0; i < 10; i++) {
threads[i].start();
}
}
}
非公平锁:表示先启动的线程不一定先获取到锁。
class MyService4{
private ReentrantLock lock;
public MyService4(boolean isFair){
lock = new ReentrantLock(isFair);
}
public void serviceMethod(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "获得到锁");
}finally {
lock.unlock();
}
}
}
public class Test4 {
public static void main(String[] args) {
final MyService4 service4 = new MyService4(false);
Runnable runnable = () -> {
System.out.println(Thread.currentThread().getName()+"运行了");
service4.serviceMethod();
};
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(runnable);
}
for (int i = 0; i < 10; i++) {
threads[i].start();
}
}
}
5,synchronized和ReentrantLock的区别
两者的共同点:
- 都是用来协调多线程对共享对象、变量的访问
- 都是可重入锁,同一线程可以多次获得同一个锁
- 都保证了可见性和互斥性
两者的不同点:
- ReentrantLock显示获得释放锁;synchronized隐式获得释放锁
- ReentrantLock可相应中断,可轮回;synchronized是不可以相应中断的
- ReentrantLock是API级别的;synchronized是JVM级别的
- ReentrantLock可以实现公平锁(默认是非公平锁);synchronized只能是非公平锁
- ReentrantLock是同步非阻塞,采用乐观并发策略;synchronized是同步阻塞,采用悲观并发策略
- ReentrantLock是一个接口;synchronized时java关键字
- ReentrantLoc在发生异常的时候不会自动释放锁;synchronized在发生异常的时候会自动释放锁
- ReentrantLock可以响应中断;synchronized不能响应中断,只能一直等待直到执行结束
- ReentrantLock无法知道是否成功获取到锁;synchronized可以知道是否成功获取到锁
- ReentrantLock通过读写锁提高效率;synchronized在任何情况下都只有一个线程可以访问这个资源
6,使用ReentrantReadWriteLock类
类ReentrantLock具有完全互斥排他的效果,即同一时间只有一个线程在执行ReentrantLock.lock()方法后后面的任务,这样可以保证线程的安全,但是这样的效率是特别低的。因此引进ReentrantReadWriteLock类,该类使用了读写锁(读锁也叫共享锁,写锁也叫排他锁),读锁之间是不互斥的,读锁和写锁之间是互斥的,写锁和写锁之间是互斥的,也就说同一时刻可以有多个线程获取到读锁来读取数据。
读锁之间的共享:同一时刻多个线程都是可以获取到这一把读锁的。
class MyService5{
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void read(){
try {
try {
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "获取到读锁");
Thread.sleep(10000);
}finally {
lock.readLock().unlock();
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class ThreadC extends Thread{
private MyService5 myService5;
public ThreadC(MyService5 myService5){
this.myService5 = myService5;
}
@Override
public void run() {
myService5.read();
}
}
class ThreadD extends Thread{
private MyService5 myService5;
public ThreadD(MyService5 myService5){
this.myService5 = myService5;
}
@Override
public void run() {
myService5.read();
}
}
public class Test5 {
public static void main(String[] args) {
MyService5 myService5 = new MyService5();
ThreadC threadC = new ThreadC(myService5);
threadC.setName("C");
ThreadD threadD = new ThreadD(myService5);
threadD.setName("D");
threadC.start();
threadD.start();
}
}
读锁和写锁之间的互斥:同一时刻只有一个线程都是可以获取到这一把写锁的,在这个时候读的时候就不能写,写的时候就不能读
class MyService5{
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void read(){
try {
try {
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "获取到读锁");
Thread.sleep(10000);
}finally {
lock.readLock().unlock();
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
public void write(){
try {
try {
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "获取到写锁");
Thread.sleep(10000);
}finally {
lock.writeLock().unlock();
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class ThreadC extends Thread{
private MyService5 myService5;
public ThreadC(MyService5 myService5){
this.myService5 = myService5;
}
@Override
public void run() {
myService5.read();
}
}
class ThreadD extends Thread{
private MyService5 myService5;
public ThreadD(MyService5 myService5){
this.myService5 = myService5;
}
@Override
public void run() {
myService5.write();
}
}
public class Test5 {
public static void main(String[] args) throws InterruptedException {
MyService5 myService5 = new MyService5();
ThreadC threadC = new ThreadC(myService5);
threadC.setName("C");
threadC.start();
Thread.sleep(1000);
ThreadD threadD = new ThreadD(myService5);
threadD.setName("D");
threadD.start();
}
}
写锁之间的互斥:同一时刻只有一个线程都是可以获取到这一把写锁的。
class MyService5{
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void write(){
try {
try {
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "获取到写锁");
Thread.sleep(10000);
}finally {
lock.writeLock().unlock();
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class ThreadC extends Thread{
private MyService5 myService5;
public ThreadC(MyService5 myService5){
this.myService5 = myService5;
}
@Override
public void run() {
myService5.read();
}
}
class ThreadD extends Thread{
private MyService5 myService5;
public ThreadD(MyService5 myService5){
this.myService5 = myService5;
}
@Override
public void run() {
myService5.read();
}
}
public class Test5 {
public static void main(String[] args) {
MyService5 myService5 = new MyService5();
ThreadC threadC = new ThreadC(myService5);
threadC.setName("C");
ThreadD threadD = new ThreadD(myService5);
threadD.setName("D");
threadC.start();
threadD.start();
}
}