高级concurrent包

线程同步:

1. 多线程读写竞争资源需要同步

2. Java语言提供了synchronized/wait/notify来实现线程的同步

3. 但是编写多线程的同步仍然很困难

所以JDK提供的高级的java.util.concurrent包

1. concurrent包可以提供更高级的同步功能

2. 同时可以简化多线程程序的编写

3. JDK1.5提供的

高级concurrent包

因为synchronized是JAVA提供的,所以不需要考虑异常.

但是如果我们使用ReentrantLock,是一个普通的JAVA类,所以我们要用try...finally的这样的结构来保证

锁的释放,

高级concurrent包

首先我们new ReentrankLock()来创建一个对象,然后我们要试图用lock.lock()方法获取锁,如果我们

获取成功,就进入try代码,在finally里面使用lock.unlock()来释放锁.这里必须要注意的是lock.lock()

必须在try之外完成.因为lock.lock()方法可能会失败,而unlock方法一定要在finally里面完成


ReentrantLock也是一种可重入的锁:

1. 可重入的锁,一个线程可多次获取同一个锁

2. lock()方法就是用来获取锁的

3. tryLock()方法可尝试获取锁并可指定超时

使用ReentrankLock比synchronized更安全

synchronized要么获得锁,要么永远的等待下去,而使用ReentrankLock的时候,我们可以使用tryLock()

在失败的时候,不会导致死锁.
package com.leon.day05;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Counter {
	
	// 首先初始化一个ReentrantLock实例,然后向上转型为Lock接口
	private Lock lock = new ReentrantLock();
	
	private int value = 0;
	
	public void add(int m) {
		lock.lock();
		try {			
			this.value += m;
		}finally {
			lock.unlock();
		}
	}
	
	public void dec(int m) {
		lock.lock();
		try {
			this.value -= m;
		}finally {
			lock.unlock();
		}
	}
	
	public int get() {
		lock.lock();
		try {
			return this.value;
		}finally {
			lock.unlock();
		}
	}
	
}

public class Main {

	final static int LOOP = 100;
	
	// 在main方法中启动两个线程,分别调用add和dec方法,
	public static void main(String[] args) throws InterruptedException {
		Counter counter = new Counter();
		Thread t1 = new Thread() {
			@Override
			public void run() {
				for(int i=0;i<LOOP;i++) {
					counter.add(1);
				}
			}
		};
		
		Thread t2 = new Thread() {
			@Override
			public void run() {
				for(int i=0;i<LOOP;i++) {
					counter.dec(1);
				}
			}
		};
		
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println(counter.get());
		
		
	}
	
}

高级concurrent包

ReentrantLock可以替代synchronized

ReentrantLock获取锁更安全

必须使用try...finally保证正确获取和释放锁

高级concurrent包

临界区就是任何时候只有一个线程能够执行的代码块,但是有些时候这种保护有点过头

任何时候只允许一个线程修改,对inc()方法加锁也是必须的,但是get()方法只读取数据,不修改数据,

允许多个线程同时调用


我们希望允许多个线程同时读,但只要一个线程在写,其他线程就必须等待.

多线程同时读是允许的,我们只是不允许多线程同时写,或者同时读写.


我们可以使用ReadWriteLock可以解决:

1. 只允许一个线程写入,(其它线程既不能写入也不能读取)

2. 但是在没有线程写入的时候,多个线程允许同时读(提高性能)

高级concurrent包

首先创建一个ReadWriteLock的实例,然后分别获取readLock()和writeLock().

变量rLock代表readLock,而wLock代表writeLock,在写方法中使用wLock.lock()加锁

而在读方法中使用rLock.lock()加锁,这样我们就可以大大的提高读取的效率.
package com.leon.day05;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

class Counter {
	
	// 首先创建一个ReadWriteLock的实例
	private ReadWriteLock lock = new ReentrantReadWriteLock();
	// 然后通过readLock获取读锁
	private Lock rLock = lock.readLock();
	// 通过writeLock获取写锁
	private Lock wLock = lock.writeLock();
	
	private int value = 0;
	
	// add方法是一个修改方法,所以用wLock,解锁加锁
	public void add(int m) {
		wLock.lock();
		try {			
			this.value += m;
		}finally {
			wLock.unlock();
		}
	}
	
	// 对于dec方法也是写锁
	public void dec(int m) {
		wLock.lock();
		try {
			this.value -= m;
		}finally {
			wLock.unlock();
		}
	}
	
	// 但是对于get方法只需要使用一个读锁,这样多个线程就可以获取读锁
	public int get() {
		rLock.lock();
		try {
			return this.value;
		}finally {
			rLock.unlock();
		}
	}
	
}

public class Main {

	final static int LOOP = 100;
	
	// 在main方法中启动两个线程,分别调用add和dec方法,
	public static void main(String[] args) throws InterruptedException {
		Counter counter = new Counter();
		Thread t1 = new Thread() {
			@Override
			public void run() {
				for(int i=0;i<LOOP;i++) {
					counter.add(1);
				}
			}
		};
		
		Thread t2 = new Thread() {
			@Override
			public void run() {
				for(int i=0;i<LOOP;i++) {
					counter.dec(1);
				}
			}
		};
		
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println(counter.get());
		
		
	}
	
}
ReadWriteLock适用的条件:

1. 适用同一个实例,有大量线程读取,仅有少数线程修改:例如一个论坛的帖子,浏览可以看成是读取,但是

非常频繁的,发布写入是不频繁的. 这种情况下就可以使用ReadWriteLock.
使用ReadWriteLock可以提高读取的效率

1. ReadWrite只允许一个线程写入

2. 但是允许多个线程同时读取

3. 特别适合读多写少的场景