【Java高并发学习】读写锁、倒计时器、LockSupport、线程池


1.ReadWriteLock读写锁

JDK5中提供了读写锁,可以有效地减少锁竞争提高性能。比如线程A1、A2、A3进行写操作,B1、B2、B3进行读操作,在使用重入锁、内部锁(synchronized)时,理论上所有的读之间、写之间、读写之间都是串行的。当B1在进行读操作时,B2、B3也得等待B1的锁资源释放,但是读操作并不会破坏数据的完整性,这种等待便没有了意义。

 
非阻塞 阻塞
阻塞 阻塞

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

/**
 * 读写锁测试
 * @author wsz
 * @date 2017年12月5日
 */
public class ReadWriteLockDemo {
	//重入锁
	private static Lock lock = new ReentrantLock();
	
	//读写锁
	private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
	private static Lock readLock = readWriteLock.readLock();
	private static Lock writeLock = readWriteLock.writeLock();
	//测试变量
	private int value;
	
	//读操作
	public Object handleRead(Lock lock) throws InterruptedException {
		try {
			lock.lock();
			Thread.sleep(1000);//模拟耗时
			return value;
		}finally {
			lock.unlock();
		}
		
	}
	
	//写操作
	public void handleWrite(Lock lock,int index) throws InterruptedException {
		try {
			lock.lock();
			Thread.sleep(1000);//模拟耗时
			this.value = index;
		}finally {
			lock.unlock();
		}
	}
	
	public static void main(String[] args) {
		final ReadWriteLockDemo demo = new ReadWriteLockDemo();
		Runnable read = new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println(demo.handleRead(readLock));
//					System.out.println(demo.handleRead(lock)); 重入锁
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};
		Runnable write = new Runnable() {
			@Override
			public void run() {
				try {
					demo.handleWrite(writeLock, new Random().nextInt());
//					demo.handleWrite(lock, new Random().nextInt()); 重入锁
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};
		
		for(int i = 0; i< 20; i++) {
			new Thread(read).start();//读线程并行
		}
		for(int i = 0; i< 5; i++) {
			new Thread(write).start();//写线程串行
		}
	}
}

2.倒计时器:CountDownLatch

可以让某一个线程等待直到倒计时结束,再开始执行。
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 倒计时器
 * @author wsz
 * @date 2017年12月5日
 */
public class CountDownLatchDemo implements Runnable{

	static final CountDownLatch cdl = new CountDownLatch(10);//参数为计数个数
	static final CountDownLatchDemo demo = new CountDownLatchDemo();

	@Override
	public void run() {
		try {
			Thread.sleep(new Random().nextInt(10)*1000);
			System.out.println("ok");
			cdl.countDown();//完成一个线程,计数-1
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		ExecutorService pool = Executors.newFixedThreadPool(10);
		for(int i = 0; i< 15;i ++) {//线程池数量<10,将一直等待;
					//线程池数量>=10 将按时打印all over,超过的次数将继续打印ok
			pool.submit(demo);
		}
		cdl.await();//主线程在CountDownLatch上等待,10次均已完成后,主线程才能继续执行
		System.out.println("all over");
		cdl.countDown();
	}
}

3.线程阻塞工具类:LockSupport

  1. 在线程内任意位置让线程阻塞
  2. 相比Thread.suspend(),弥补了由于resume()在前发生,导致线程无法继续执行的情况
  3. 相比Object.wait(),不需要先获得某个对象的锁,也不会抛出InterruptedException异常
  4. LockSupport.unpark(Runnable target)可以阻塞线程,此外还提供了限时的等待方法
  5. 使用类似信号量的机制,为每一个线程准备一个许可,如果许可可用,park()函数会立即返回,并消费这个许可,如果不可用,就会阻塞。unpark()则使一个许可变为可用(许可不可累加)。
  6. 处于park()挂起状态的线程为WAITING
import java.util.concurrent.locks.LockSupport;

/**
 * LockSupport案例
 * 依然无法保证unpark()方法发生在park()方法之后。
 * LockSupport使用类似信号量的机制。它为每一个线程提供一个许可,
 * 如果许可可用,park()函数会立即返回,并消费这个许可(将许可变为不可用)
 * 如果许可不可用,就会阻塞,unpark()使得一个许可变为可用(但不可累加)
 * @author wsz
 * @date 2017年12月5日
 */
public class LockSupportDemo {

	public static Object u = new Object();
	static ChangeObjectThread t1  = new ChangeObjectThread("t1");
	static ChangeObjectThread t2  = new ChangeObjectThread("t2");
	
	public static class ChangeObjectThread extends Thread{
		
		public ChangeObjectThread(String name) {
			super.setName(name);
		}
		
		@Override
		public void run() {
			synchronized(u) {
				System.out.println("in "+getName());
				LockSupport.park();
			}
			
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		
		t1.start();
		Thread.sleep(1000);
		t2.start();
		LockSupport.unpark(t1);//进行阻塞,状态变为WAITING
		LockSupport.unpark(t2);//进行阻塞,状态变为WAITING
		t1.join();
		t2.join();
	}

}

4.线程池

【Java高并发学习】读写锁、倒计时器、LockSupport、线程池_第1张图片

import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 定时任务
 * 如果任务程序本身抛出异常,后续所有执行都将被中断
 * @author wsz
 * @date 2017年12月5日
 */
public class ScheduledExecutorServiceDemo {

	public static void main(String[] args) {

		ScheduledExecutorService ses = Executors.newScheduledThreadPool(10);
		/**
		 * scheduleAtFixedRate保证之前的任务已完成
		 * 当操作时间>调度周期2s时,后一个任务会立即执行
		 */
		ses.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				try {
					Thread.sleep(1000);//模拟操作执行1s
					System.out.println(new Random().nextInt());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, 3, 2, TimeUnit.SECONDS);//3初始延迟,每2秒执行一次
	}
}




你可能感兴趣的:(Java高并发)