关于java多线程的概念以及基本用法:java多线程基础
4,Lock的使用
- ReentrantLook类的使用
- ReentrantReadWriteLock类的使用
4.1,ReentrantLook类的使用
新建MyService类:
public class MyService {
private Lock lock = new ReentrantLock();
public void testMethod() {
lock.lock();//获取锁
for(int i=0;i<5;i++) {
System.out.println("ThreadName="+Thread.currentThread().getName()+(" "+(i+1)));
}
lock.unlock();//解锁
}
}
MyThread类:
public class MyThread extends Thread{
private MyService service;
public MyThread(MyService service) {
super();
this.service = service;
}
@Override
public void run() {
service.testMethod();
}
}
测试类:
public class Run {
public static void main(String[] args) {
MyService service = new MyService();
MyThread a1 = new MyThread(service);
MyThread a2 = new MyThread(service);
MyThread a3 = new MyThread(service);
MyThread a4 = new MyThread(service);
MyThread a5 = new MyThread(service);
a1.start();
a2.start();
a3.start();
a4.start();
a5.start();
}
}
结果:
ThreadName=Thread-1 1
ThreadName=Thread-1 2
ThreadName=Thread-1 3
ThreadName=Thread-1 4
ThreadName=Thread-1 5
ThreadName=Thread-3 1
ThreadName=Thread-3 2
ThreadName=Thread-3 3
ThreadName=Thread-3 4
ThreadName=Thread-3 5
ThreadName=Thread-2 1
ThreadName=Thread-2 2
ThreadName=Thread-2 3
ThreadName=Thread-2 4
ThreadName=Thread-2 5
ThreadName=Thread-0 1
ThreadName=Thread-0 2
ThreadName=Thread-0 3
ThreadName=Thread-0 4
ThreadName=Thread-0 5
ThreadName=Thread-4 1
ThreadName=Thread-4 2
ThreadName=Thread-4 3
ThreadName=Thread-4 4
ThreadName=Thread-4 5
由结果可以看到一个线程打印完将锁释放,其他线程才可以继续打印,但是线程间的顺序是随机的
4.2,Condition实现等待/通知
关键字synchronized与wait()和notify()/notifyAll()方法结合可以实现等待/通知模式,ReentrantLock借助Condition对象可以实现相同的功能。一个Lock对象里面可以创建多个Condition(即对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以自由选择线程通知,使用notify()/notifyAll()方法进行通知时,被通知的线程是由JVM随机选择的
新建MyService类:
public class MyService {
private Lock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
public void await() {
try {
lock.lock();
System.out.println("wait 时间为:"+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();
}
}
}
ThreadA类:
public class ThreadA extends Thread{
private MyService service;
public ThreadA(MyService service) {
super();
this.service = service;
}
@Override
public void run() {
service.await();
}
}
测试类:
public class Run {
public static void main(String[] args) throws InterruptedException {
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.start();
Thread.sleep(3000);
service.signal();
}
}
结果:
wait 时间为:1534645646731
signal时间为:1534645649731
Objectl类中wait()方法相当于Condition类中的await()方法
Objectl类中notify()方法相当于Condition类中的signal()方法
4.3,多个Condition实现通知部分线程
新建MyService类:
public class MyService {
private Lock lock = new ReentrantLock();
public Condition conditionA = lock.newCondition();
public Condition conditionB = lock.newCondition();
public void awaitA() {
try {
lock.lock();
System.out.println("begin awaitA时间为:"+System.currentTimeMillis()
+"ThreadName="+Thread.currentThread().getName());
conditionA.await();
System.out.println("end awaitA时间为:"+System.currentTimeMillis()
+"ThreadName="+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void awaitB() {
try {
lock.lock();
System.out.println("begin awaitB时间为:"+System.currentTimeMillis()
+"ThreadName="+Thread.currentThread().getName());
conditionB.await();
System.out.println("end awaitB时间为:"+System.currentTimeMillis()
+"ThreadName="+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void signalAll_A() {
try {
lock.lock();
System.out.println("signalAll_A时间为:"+System.currentTimeMillis()
+"ThreadName="+Thread.currentThread().getName());
conditionA.signalAll();
} finally{
lock.unlock();
}
}
public void signalAll_B() {
try {
lock.lock();
System.out.println("signalAll_B时间为:"+System.currentTimeMillis()
+"ThreadName="+Thread.currentThread().getName());
conditionB.signalAll();
} finally{
lock.unlock();
}
}
}
两个线程类:
public class ThreadA extends Thread{
private MyService service;
public ThreadA(MyService service) {
super();
this.service = service;
}
@Override
public void run() {
service.awaitA();
}
}
public class ThreadB extends Thread {
private MyService service;
public ThreadB(MyService service) {
super();
this.service = service;
}
@Override
public void run() {
service.awaitB();
}
}
测试类:
public class Run {
public static void main(String[] args) throws InterruptedException{
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadB b= new ThreadB(service);
b.setName("B");
b.start();
Thread.sleep(3000);
service.signalAll_A();
}
}
结果:
begin awaitA时间为:1534648259058ThreadName=A
begin awaitB时间为:1534648259058ThreadName=B
signalAll_A时间为:1534648262059ThreadName=main
end awaitA时间为:1534648262059ThreadName=A
4.4,公平锁与非公平锁
锁Lock分为公平锁与非公平锁,公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,非公平锁是一种获取锁的抢占机制,是随机获得锁的
新建Service类:
public class Service {
private ReentrantLock lock;
public Service(boolean isFair) {
super();
lock = new ReentrantLock(isFair);
}
public void serviceMethod() {
try {
lock.lock();
System.out.println("ThreadName="+Thread.currentThread().getName()+"获得锁定");
}finally{
lock.unlock();
}
}
}
RunFair类:
public class RunFair {
public static void main(String[] args) throws InterruptedException{
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();
}
}
}
RunNoFair类:
public class RunNoFair {
public static void main(String[] args) throws InterruptedException{
final Service service = new Service(false);
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();
}
}
}
运行结果:
RunFair类:基本有序
RunNoFair类:基本无序
4.5,lock的一些方法
- 方法int getHoldCount()的作用是查询当前线程保持此锁定的个数,也就是锁的个数
- 方法int getQueueLength()的作用是返回正等待获取此锁定的线程估计数,也就是等待锁的线程
- 方法int getWaitQueueLength(Condition condition)的作用是返回等待与此锁定相关的给定条件condition的线程估计数,也就是执行了同一个condition对象的await()方法的线程数
- 方法boolean hasQueuedThread(Thread thread)的作用是查询指定的线程是否正在等待获取此线程
- 方法 boolean hasQueuedThreads()的作用是查询是否有线程正在等待获取此锁定
- 方法boolean hasWaiters(Condition condition)的作用是查询是否有线程正在等待与此锁定有关的condition条件
- 方法boolean isFair()的作用是判断是不是公平锁
- 方法boolean isHeldByCurrentThread()的作用是查询当前线程是否保持此锁定
- 方法boolean isLocked()的作用是查询此锁定是否由任意线程保持
- 方法 void lockInterruptibly()的作用是:如果当前线程未被中断,则获取锁定,否则出现异常
- 方法boolean tryLock()的作用是:仅在调用时锁定未被另一个线程保持的情况下,才获取该锁定
- 方法 boolean tryLock(long timeout,TimeUnit unit)的作用是,如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定
使用Lock+Condition实现线程顺序执行:点这里
4.6,ReentranReadWriteLock类
同一时间只有一个线程在执行ReentranLock.lock()方法后面的任务,这样确实保证了线程的安全,但是效率很低,ReentranReadWriteLock类可以提高效率
读写锁有两个锁,一个读操作相关的锁,也称为共享锁,一个写操作相关的锁,也称为排他锁。读锁与写锁互斥,写锁与写锁互斥,但是读锁与读锁不互斥,也就是没写操作的情况下可以进行很多的读操作
1,读读共享
Service类:
public class Service {
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 class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
public void run() {
service.read();
}
}
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
public void run() {
service.read();
}
}
测试类:
public class Run {
public static void main(String[] args) {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.setName("A");
ThreadB b= new ThreadB(service);
b.setName("B");
a.start();
b.start();
}
}
结果:
获得锁B 1534681602545
获得锁A 1534681602545
由结果可以看到,两个线程几乎同时进入lock()后的代码
2,写写互斥
保持上面代码不变,更改Service类:
public class Service {
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();
}
}
}
结果:
获得写锁A 1534681950514
获得写锁B 1534681960516
显然写操作同一时间只允许一个线程执行
3,读写互斥
更改Service类和测试类:
public class Service {
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();
}
}
}
public class Run {
public static void main(String[] args) throws InterruptedException {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
Thread.sleep(1000);
ThreadB b= new ThreadB(service);
b.setName("B");
b.start();
}
}
结果:
获得锁A 1534682422325
获得锁B 1534682423326
4,写读互斥
更改测试类;
public class Run {
public static void main(String[] args) throws InterruptedException {
Service service = new Service();
ThreadB b= new ThreadB(service);
b.setName("B");
b.start();
Thread.sleep(1000);
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
}
}
结果:
获得锁B 1534682527683
获得锁A 1534682528683
综上:只有读读操作是异步且非互斥的,其他的都是互斥的