java-并发专题-1

1.并发、并行、吞吐量

并发:是指单位时间内(通常1s)系统或程序处理的请求数量。

并行:多个线程或进程同时运行完成某个大型计算的一部分。

吞吐量:网络吞吐量是指定单位时间内传输的数据字节大小,系统吞吐量与并发差不多一个意思。

2.锁

隐式锁:synchronized(同步关键字)

最优用法:

private byte[] lock = new byte[1];

public void myFunc() {
    synchronized(lock){
        //...
    }
}

显示锁:Lock

显示锁与隐式锁的区别:

1.Lock只适用于代码块,synchronized对象之间互斥。

2.Lock锁粒度更细,因此线程的开销可能会更小一些,性能更佳,但需要手动开启并释放锁。

ReentrantLock:重入锁

是Lock接口的一个实现类。

final Lock lock = new ReentrantLock();

public void func(){
    try{
        lock.lock();
    }catch(Exception e){
        //...
    }finally{
        lock.unlock();
    }
}

ReentrantReadWriteLock:读写锁

是ReadWriteLock接口唯一实现类,内部维护了一个读Lock和写Lock

基本使用示例:

	public static void main(String[] args) {
		DefaultContainer defaultContainer = new DefaultContainer();
		
		for(int i = 1; i <= 4; i ++) {
			new Thread(() ->{
				defaultContainer.read();
			}, "thread-read-" + i).start();
		}
		
		for(int i = 1; i <= 2; i ++) {
			new Thread(() ->{
				defaultContainer.write();
			}, "thread-write-" + i).start();
		}
		
		try {
			Thread.sleep(2100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	private static class DefaultContainer {
		
		private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);	// true表示公平性
		
		public void write() {
			try {
				lock.writeLock().lock();	//上写锁
				System.out.println(Thread.currentThread().getName() + "->writed start");
				Thread.sleep(1000);
				System.out.println(Thread.currentThread().getName() + "->writed over");
			}catch (Exception e) {
				e.printStackTrace();
			}finally {
				lock.writeLock().unlock();
			}
		}
		
		public void read() {
			try {
				lock.readLock().lock();	//上读锁
				System.out.println(Thread.currentThread().getName() + "->read start");
				Thread.sleep(100);
				System.out.println(Thread.currentThread().getName() + "->read over");
			}catch (Exception e) {
				e.printStackTrace();
			}finally {
				lock.readLock().unlock();
			}
		}
		
	}

从示例中不难看出,读-读之间并行执行,读-写,写-写串行

读写锁特性:

1.公平性:即读-写,写-写根据阻塞时间长短来顺序获取锁,读-读之间没有锁竞争因此不存在该特性。

 public ReentrantReadWriteLock(boolean fair) //该构造函数中可以指定是否使用公平策略。公平性吞吐量小于非公平性

2.重入性:允许按照顺序再次获取锁的特性,获取写锁后可以再次获取读锁,但是获取读锁后不可以再获取写锁。

3.锁降级:获取写锁后,再获取读锁,然后释放写锁,当前线程就由写变成了读,谓之锁降级。

4.锁升级:没有这个概念,读取锁无法升级到写入锁,因为获取写入锁之前需要释放所有的读、写锁。

5.重入数:读、写锁的最大数量分别是65535。

StampedLock:悲观、乐观锁(后续补充)

3.volatile(内存可见性)和重排序

volatile:可见性

这里注意一下,该关键字修饰的变量只满足内存可见性,不满足原子性,即适用于并发读,单个写的应用场景。

因此如果有并发写的场景,volatile也无法做到原子操作,还是需要加锁。

执令重排序:

private int a = 0;
private boolean flag = false;

public void write() throws InterruptedException {
    a =1;
    flag = true;
}

public int read() throws InterruptedException {
    int result = 0;
    if(flag) {
        result = a*a;
    }
    return result;
}

如果a=1和flag=true的执行顺序重排,那么read()线程的执行结果会发生改变,使用volatile修饰a和flag即可防止重排序

4.ThreadLocal (当前线程副本)

应用场景:

        当多个线程需要操作共享变量时,相互之间的操作又不能彼此影响时,使用ThreadLocal

ThreadLocal实现原理:

        首先ThreadLocal会维护一个初始的共享变量

        protected T initialValue();通过重写ThreadLocal的该方法可以初始化共享变量

        线程中通过ThreadLocal对象的get方法可以获取到当前共享变量的一个副本。

        查看Thread相关源码可知,每个线程thread中都维护一个ThreadLocalMap对象,ThreadLocalMap是Thread的一个静态内部类,结构类似Map的key-value结构,key就是threadLocal对象,value则是共享变量的一份拷贝。

在threadLocal.get()的时候,实际获取的是当前线程的一个ThreadLocalMap,如果该map存在,则直接返回,即可拿到value。如果不存在,则会new一个新的ThreadLocalMap并赋值给当前线程中的ThreadLocalMap对象,同时把初始的value存到ThreadLocalMap中,key则是threadLocal对象,然后再返回初始的value。

大致如下图:

ThreadLocal使用时注意问题:

如果是在线程池中的线程使用ThreadLocal,注意在当前线程最后一次使用完后要remove(),否则线程池可能会重复利用同一个线程,那么ThreadLocal就会保留之前的数据,导致数据混乱。

例如javaweb中tomcat中的http线程就是线程池创建出来的会复用,因此一定要记得remove。

5.atomic原子操作

基本类型:AtomicInteger、AtomicLong、AtomicBoolean

比较常用的就是上面几种基本类型的,常用于多线程环境下替代加锁,底层依赖于C实现的基于CPU的CAS操作(比较交换)

6.并发集合类

并发Map:ConcurrentHashMap   替代HashTable

并发List:CopyOnWriteArrayList   线程安全的ArrayList

并发Set:CopyOnWriteArraySet   线程安全的Set基于CopyOnWriteArrayList

7.阻塞队列BlockingQueue

常用于生产者消费者应用模型

常用API比较
插入方法移除方法
boolean add(E e);队列满则抛异常boolean remove(Object o);队列空则抛异常
boolean offer(E e);立即返回(不常用)E poll();立即返回队头元素,队列空则返回null
void put(E e);队列满一直阻塞E take();队列空则一直阻塞
boolean offer(E e, long timeout, TimeUnit unit);队列满,则阻塞至超时返回falseE poll(long timeout, TimeUnit unit);队列空,阻塞至超时返回null

*  ArrayBlockingQueue基于数组:适用于读多写少

*  LinkedBlockingQueue基于链表:适用于读<=写

*  以上比较是基于他们内部维护的队列模型,基于生产者消费者模型的实际开发中多数时候还是用LinkedBlockingQueue,

   该队列内部基于读写锁处理多线程数据安全,而ArrayBlockingQueue内部读写都使用了同一个锁,此外使用LinkedBQ

   时最好指定大小,防止内存溢出,通常情况下,队列里存的数据都是类似索引的小数据,因此实际中可能不指定大小。

*  PriorityBlockingQueue基于数组:有序的阻塞队列

    该队列可以按照某种顺序来存取元素,保证顺序的方式是,构造队列时加入一个比较器。

    另外该队列只会阻塞消费者,因此当生产速率大于消费时,可能会内存溢出,并且该队列没有大小限制。

    构造方法:PriorityBlockingQueue(int initialCapacity, Comparator<? super E> comparator);initialCapacity该参数是队列的

    初始容量大小,并不是指队列的大小,comparator是一个比较器接口,如下构建一个初始大小1000,按降序排序的阻塞队列

使用示例:

public class PriorityBlockQueueDemo {
	
	final static Random random = new Random(23);

	public static void main(String[] args) {
		
		int initialCapacity = 1000;
		
		/*
		 * 此处构造队列时,传入Comparator
		 * 也可以Person类直接实现比较器接口,则构造队列时无需传入
		 */
		PriorityBlockingQueue<Person> priorityBlockingQueue = new PriorityBlockingQueue<Person>(initialCapacity, (t1, t2) ->{
			return t1.age >= t2.age ? 1 : -1;	//注意该接口的排序规则,此处为按年龄升序,降序1和-1调换
		});
		
		for(int i = 0; i < 100; i ++) {
			int nextInt = random.nextInt(55);
			Person person = new Person().setName("name" + i).setAge(nextInt);
			priorityBlockingQueue.put(person);
		}
		
		System.out.println(priorityBlockingQueue.toString());
		
	}
	
	static class Person {
		private int age;
		private String name;
		public int getAge() {
			return age;
		}
		public Person setAge(int age) {
			this.age = age;
			return this;
		}
		public String getName() {
			return name;
		}
		public Person setName(String name) {
			this.name = name;
			return this;
		}
		@Override
		public String toString() {
			return "{age: " + age + ", name: '" + name + "'}";
		}
		
	}
	
}

*   DelayQueue<E extends Delayed> 延时获取队列

    该队列是一个支持优先级(即有序,会按照入队元素的有效时间进行排序)及延时获取的队列。

    该队列的put方法不阻塞,即该队列无界,因此需要注意内存溢出的可能性。

    该队列中的元素必须实现Delayed extends Comparable<Delayed >接口的两个方法如下:

    int compareTo(T o);    //该方法需要对元素的有效时间进行升序排序,保证时间早的先出队

    long getDelay(TimeUnit unit);    //该接口返回的是 当前元素的有效时间与当前系统时间差,如果差为0,则队列poll(long timeout, TimeUnit unit)方法会获取到该元素,否则会阻塞timeout

    DelayQueue使用场景如下:

    1.缓存系统key的失效,可以将key及其有效时间放入DelayQueue,起一个线程循环获取DelayQueue中的超时key,并从缓存中       移除。

    2.一次性的定时任务,可以将任务ID及任务的执行时间放入DelayQueue,超时则取出任务ID并执行任务。

    DelayQueue缓存系统的使用示例如下:

public class DelayQueueDemo {

	public static void main(String[] args) {
		final MemoryCache memoryCache = new MemoryCache(10000);
		System.out.println(memoryCache.size());
		final Random random = new Random();
		for (int i = 1; i <= 1000; i++) {
			String key = "test" + i;
			String value = key + "-value";
			try {
				memoryCache.put(key, value, 10 + random.nextInt(21), TimeUnit.SECONDS);
				memoryCache.put(key + "b", value);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

	}

	// 缓存模型
	static class MemoryCache {
		final byte[] lock = new byte[0];
		final DelayQueue<Delayed> delayQueue = new DelayQueue<Delayed>();
		final ConcurrentHashMap<String, Object> container = new ConcurrentHashMap<String, Object>();
		private long size = Integer.MAX_VALUE;
		
		public MemoryCache() {
			this.init();
		}
		
		public MemoryCache(long size) {
			this.size = size;
			this.init();
		}

		public void init() {
			new Thread(() -> {
				while (true) {
					try {
						System.out.println(
								"delayQueue.size=" + delayQueue.size() + ",container.size=" + container.size()); // 间隔1秒打印队列元素

						Delayed delayed = delayQueue.poll(1, TimeUnit.SECONDS); // 队列为空则阻塞1秒
						if (delayed != null) {
							ClearMemoryCacheTask task = (ClearMemoryCacheTask) delayed;
							String memoryCacheKey = task.getMemoryCacheKey();
							container.remove(memoryCacheKey);
							System.out.println("container.remove(" + memoryCacheKey + ")");
						}

					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}).start();
		}
		
		public long size() {
			return container.mappingCount();
		}

		// put时设置过期时间
		public void put(String key, Object value, long timeout, TimeUnit timeUnit) throws Exception {
			if(size() >= this.size) {	//此处需校验是否超过指定的缓存的最大size
				throw new RuntimeException("MemoryCache is exceed, key=" + key);
			}
			if (timeout > 0 && timeUnit != null) {
				long sourceDuration = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(timeout, timeUnit);
				delayQueue.put(new ClearMemoryCacheTask(key, sourceDuration));
			}
			container.put(key, value);

		}

		// 永不过期
		public void put(String key, Object value) throws Exception {
			put(key, value, -1, null);
		}

		public Object get(String key) {
			return container.get(key);
		}

		private class ClearMemoryCacheTask implements Delayed {

			private String memoryCacheKey;

			private long sourceDuration;

			public ClearMemoryCacheTask(String memoryCacheKey, long sourceDuration) {
				this.memoryCacheKey = memoryCacheKey;
				this.sourceDuration = sourceDuration;
			}

			public String getMemoryCacheKey() {
				return memoryCacheKey;
			}

			/*
			 * 比较器接口:比较队列当中的ClearMemoryCacheTask的源过期时间
			 * 此处比较规则是:源过期时间小的排到队头,只有这样才可以保证队列按时间先后移除ClearMemoryCacheTask
			 */
			@Override
			public int compareTo(Delayed o) {
				ClearMemoryCacheTask task = (ClearMemoryCacheTask) o;
				// 此处比较规则是大的往队尾排,1和-1调换则反之。
				return this.sourceDuration > task.sourceDuration ? 1
						: (this.sourceDuration < task.sourceDuration ? -1 : 0);
			}

			/*
			 * 该接口返回的值应该为:源过期时间 - 当期时间 (注意单位,此处使用的毫秒数)
			 * 该接口返回值为0时,才会从当期队列中获取到ClearMemoryCacheTask
			 */
			@Override
			public long getDelay(TimeUnit unit) {
				return this.sourceDuration - System.currentTimeMillis();
			}

			@Override
			public String toString() {
				return JSONObject.toJSONString(this);
			}

		}
	}

}

*   SynchronousQueue<T> 同步队列

    该队列不存储数据。

    公平模式,按照FIFO阻塞多余的生产者或消费者,非公平模式,可能导致某些生产者或消费者线程饥渴,默认采用非公平模式。

    如果只是为了做生产者和消费者之间的数据传递,SynchronousQueue性能要优于LinkedBlockingQueue和ArrayBlockingQueue。

使用示例:

public class SynchronousQueueDemo {

	public static void main(String[] args) {
		SynchronousQueue<String> synchronousQueue = new SynchronousQueue<String>();
		new Thread(new ProducerTask(synchronousQueue)).start();
		for(int i = 0; i < 10; i ++) {
			new Thread(new ConsumerTask(synchronousQueue), "t" + i).start();
		}
	}

	static class ProducerTask implements Runnable {

		private SynchronousQueue<String> synchronousQueue;

		public ProducerTask(SynchronousQueue<String> synchronousQueue) {
			this.synchronousQueue = synchronousQueue;
		}
		
		@Override
		public void run() {
			while (true) {
				try {
					Thread.sleep(10);	//休眠是为了System.currentTimeMillis()不会重复
					this.synchronousQueue.put((System.currentTimeMillis() + "").substring(8));
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

	}

	static class ConsumerTask implements Runnable {

		private SynchronousQueue<String> synchronousQueue;

		public ConsumerTask(SynchronousQueue<String> synchronousQueue) {
			this.synchronousQueue = synchronousQueue;
		}

		@Override
		public void run() {
			while (true) {
				try {
					String take = this.synchronousQueue.take();
					print(take);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

		private void print(String msg) throws InterruptedException {
			Thread.sleep(500);	//模拟消费耗时
			System.out.println(Thread.currentThread().getName() + "->" + msg);
		}

	}

}

*    LinkedBlockingDeque 双端链表阻塞队列(后续跟进)

*    LinkedTransferQueue 链表传输队列(后续跟进)

8.同步计数器

1.CountDownLatch  线程倒计数器

应用于主线程等待多个子线程执行结果或完成的场景,类似于Future.get()方法,会阻塞线程池中对应线程。

常用API:

CountDownLatch(int count);//count表示倒计数的个数

void countDown();//子任务结束后调用该方法将线程数减1

void await();//阻塞至count个子任务执行结束

boolean await(long timeout, TimeUnit unit);阻塞至count个子任务执行结束或超时返回false

简单实用,示例如下:

public class CountDownLatchDemo {

	public static void main(String[] args) {
		final CountDownLatch countDownLatch = new CountDownLatch(3);
		DefaultTask defaultTask = new DefaultTask(countDownLatch);
		System.out.println("start");
		for(int i = 1; i < 4; i ++) {
			new Thread(defaultTask, "t-" + i).start();
		}
		try {
			countDownLatch.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("end");
	}
	
	static class DefaultTask implements Runnable{
		
		final CountDownLatch countDownLatch;
		final Random random = new Random();
		
		public DefaultTask(CountDownLatch countDownLatch) {
			this.countDownLatch = countDownLatch;
		}

		@Override
		public void run() {
			try {
				this.doSome();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			this.countDownLatch.countDown();
		}
		
		private void doSome() throws InterruptedException {
			String name = Thread.currentThread().getName();
			System.out.println(name + "doSome...start");
			long millis = 500 + random.nextInt(500);
			Thread.sleep(millis);
			System.out.println(name + "doSome...end," + "耗时:" + millis);
		}
		
		
	}
	
}

2.Semaphore 信号量

应用场景:多个线程需要排队执行,并且可以同时执行多个线程,比如10个线程,每次都执行其中的5个,可以起到限流的效果。

常用API:

Semaphore(int permits);//构造信号量,并初始化信号个数permits。

Semaphore(int permits, boolean fair);//fair=true设置信号量为公平策略,公平策略下,线程会被依次放入一个FIFO队列,依次,默认false或不设置则为非公平策略。

void acquire();//从Semaphore当中获取一个许可,此刻如果许可已全部使用,则阻塞到某个线程释放出一个许可为止。

boolean tryAcquire(long timeout, TimeUnit unit);//在获取到一个许可前阻塞timeout时间,则返回false,否则获取到一个许可,返回true。

boolean tryAcquire(int permits, long timeout, TimeUnit unit);同上,获取多个许可

int availablePermits();//返回可用的许可个数

int drainPermits();//获取Semaphore的所有许可,返回值为当前获取到的许可个数,该方法的意义在于两点:一,返回当前可用的许可个数;二,将Semaphore的的许可清空,即Semaphore不可用直到释放其中的一个或多个许可。

protected void reducePermits(int reduction);//动态减少许可数,子类重写该方法,可调用(待后续跟进)

void release();//释放一个许可

void release(int permits);//释放给定数目的许可

boolean isFair();//返回Semaphore的公平策略,true表示公平

int getQueueLength();//返回等待获取许可的线程个数

简单示例:

public class SemaphoreDemo {

	volatile static int total = 0;
	
	public static void main(String[] args) {
	
		Semaphore semaphore = new Semaphore(5, false);
		for(int i = 1; i < 51; i ++) {
			new Thread(() ->{
				int count = 0;
				while(true) {
					try {
						semaphore.acquire();
						count ++;
						total ++;
						
						/*
						 * 分别设置Semaphore为公平、非公平策略,观察count的变化,可以很明显看出公平模式下,会按照线程先后次序入队
						 */
						System.out.println(Thread.currentThread().getName() + "-拿到了信号量,并开始执行第-" + count + "-次" + ",total=" + total);
						Thread.sleep(1000);
						semaphore.release();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}, "t" + i).start();
			
		}
		
	}
	
}

3.CyclicBarrier 循环屏障

*    允许一组相同task的线程执行到某个点时,彼此相互等待,当所有线程都到达这个点时,再并行向下执行。

*    CountDownLatch是主线程等待各个子线程,而CyclicBarrier是各个子线程相互等待,而主线程并不等待。

*    CountDownLatch只能使用一次,而CyclicBarrier可以在各个子线程到达后,调用reset()方法重置,再次调用await设置等待点

常用API:

CyclicBarrier(int parties);//parties为构造时,指定的屏障线程个数,一经设置,无法改变。

CyclicBarrier(int parties, Runnable barrierAction);//barrierAction这是一个回调线程task,子线程都到达await点后,会回调。

int await();//设置一个屏障点,返回值为当前子线程到达屏障点的顺序索引,0表示最后一个到达,parties-1表示第一个到达。

int await(long timeout, TimeUnit unit);//同上,在timeout时间内如果有线程还未到达,则抛出超时异常。

int getNumberWaiting();//返回已经到达屏障等待的子线程个数。

int getParties();//返回构造时指定的屏障线程个数parties常量。

void reset();//重置屏障,可以再次调用await()方法设置屏障。

 

示例:模拟如下场景,来运行一个程序

       大家一起开始去打饭

        Marry:打饭结束,开始等待其他人...
        Lucy:打饭结束,开始等待其他人...
        jack:打饭结束,开始等待其他人...
        
        所有人打饭结束,大家一起开始吃午饭
        
        Lucy:已吃完饭,开始等待其他人...
        Marry:已吃完饭,开始等待其他人...
        jack:已吃完饭,开始等待其他人...
        
        所有人已吃饭结束

默认实现:

public class CyclicBarrierDemo {

	static class HaveLunchTask implements Runnable {

		private CyclicBarrier cyclicBarrier;

		public HaveLunchTask(CyclicBarrier cyclicBarrier) {
			this.cyclicBarrier = cyclicBarrier;
		}

		// 打饭逻辑
		private void takeMeals(String personName) throws InterruptedException {
			Thread.sleep((1 + new Random().nextInt(5)) * 1000);
			System.out.println(personName + ":打饭结束,开始等待其他人...");
		}

		// 吃饭逻辑
		private void haveLunch(String personName) throws InterruptedException {
			Thread.sleep((1 + new Random().nextInt(5)) * 1000);
			System.out.println(personName + ":已吃完饭,开始等待其他人...");
		}

		@Override
		public void run() {
			try {
				String personName = Thread.currentThread().getName();
				// 开始打饭
				takeMeals(personName);

				// 等待其他人打饭结束
				cyclicBarrier.await();

				// 开始吃饭
				haveLunch(personName);
				
				// 等待其他人吃饭结束
				cyclicBarrier.await();
			} catch (InterruptedException | BrokenBarrierException e) {
				e.printStackTrace();
			}
		}
	}

	public static void main(String[] args) {
		CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new Runnable() {

			private int count = 0;	//标记回调线程执行的次数

			//回调线程
			@Override
			public void run() {
				count++;
				process(count);
			}

			//根据state判断是第几次到达屏障,来执行不同的逻辑
			private void process(int state) {
				switch (state) {
				case 1:
					System.out.println("\r\n所有人打饭结束,大家一起开始吃午饭\r\n");
					break;
				case 2:
					System.out.println("\r\n所有人已吃饭结束");
					break;
				default:
					break;
				}
			}
		});
		
		ArrayList<String> list = new ArrayList<String>();
		list.add("Marry");
		list.add("jack");
		list.add("Lucy");
		
		//程序起点
		System.out.println("大家一起开始去打饭\r\n");
		for (int i = 0; i < list.size(); i++) {
			new Thread(new HaveLunchTask(cyclicBarrier), list.get(i)).start();
		}
	}

}

 

实现二:不使用CyclicBarrier构造回调线程,手动启动一个监控线程

package cn.qu.mv.concurrent;

import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicInteger;

public class CyclicBarrierDemo {

	/**
	 * 该类继承CyclicBarrier
	 * 并增加一个方法
	 * 		boolean awaitForEndAll() 是否所有CyclicBarrier屏障的线程已经执行完毕, 如果atomic不小于parties, 则认为CyclicBarrier-> 所有相关线程await完毕。
	 * 复写一个方法
	 * 		int await() 在访问完父类CyclicBarrier的await()方法后,对atomic++
	 * @author vn03jxv
	 *
	 */
	static class MyCyclicBarrier extends CyclicBarrier {

		private final byte[] lock = new byte[0];

		AtomicInteger atomicInteger = new AtomicInteger();

		public MyCyclicBarrier(int parties) {
			super(parties);
			this.atomicInteger.set(0);
		}

		public MyCyclicBarrier(int parties, Runnable barrierAction) {
			super(parties, barrierAction);
			this.atomicInteger.set(0);
		}

		public int await() throws InterruptedException, BrokenBarrierException {
			int await = super.await();
			this.atomicInteger.incrementAndGet();
			return await;
		}

		public boolean awaitForEndAll() {
			if (this.atomicInteger.get() >= super.getParties()) {
				synchronized (lock) {
					if (this.atomicInteger.get() >= super.getParties()) {
						this.atomicInteger.set(0);
						return true;
					}
				}
			}
			return false;
		}

	}

	static class HaveLunchTask implements Runnable {

		private CyclicBarrier cyclicBarrier;

		public HaveLunchTask(CyclicBarrier cyclicBarrier) {
			this.cyclicBarrier = cyclicBarrier;
		}

		// 打饭逻辑
		private void takeMeals(String personName) throws InterruptedException {
			Thread.sleep((1 + new Random().nextInt(5)) * 1000);
			System.out.println(personName + ":打饭结束,开始等待其他人...");
		}

		// 吃饭逻辑
		private void haveLunch(String personName) throws InterruptedException {
			Thread.sleep((1 + new Random().nextInt(5)) * 1000);
			System.out.println(personName + ":已吃完饭,开始等待其他人...");
		}

		@Override
		public void run() {
			try {
				String personName = Thread.currentThread().getName();
				// 开始打饭
				takeMeals(personName);

				// 等待其他人打饭结束
				cyclicBarrier.await();

				// 开始吃饭
				haveLunch(personName);
				cyclicBarrier.await();
			} catch (InterruptedException | BrokenBarrierException e) {
				e.printStackTrace();
			}
		}
	}

	/*main方法正常输出如下:
	  	大家一起开始去打饭

		Marry:打饭结束,开始等待其他人...
		Lucy:打饭结束,开始等待其他人...
		jack:打饭结束,开始等待其他人...
		
		所有人打饭结束,大家一起开始吃午饭
		
		Lucy:已吃完饭,开始等待其他人...
		Marry:已吃完饭,开始等待其他人...
		jack:已吃完饭,开始等待其他人...
		
		所有人已吃饭结束		
	 */
	public static void main(String[] args) {
		MyCyclicBarrier cyclicBarrier = new MyCyclicBarrier(3);
		ArrayList<String> list = new ArrayList<String>();
		list.add("Marry");
		list.add("jack");
		list.add("Lucy");

		//2.启动一个监控线程
		new Thread(() -> {
			while (!cyclicBarrier.awaitForEndAll()) {	//当Marry、jack、Lucy三个人都打饭完成,则程序向下执行
			}
			System.out.println("\r\n所有人打饭结束,大家一起开始吃午饭\r\n");
			cyclicBarrier.reset();	//重置cyclicBarrier,以便复用
			while (!cyclicBarrier.awaitForEndAll()) {	//当Marry、jack、Lucy三个人都吃饭结束,则程序向下执行
			}
			System.out.println("\r\n所有人已吃饭结束");
		}).start();
		
		//1.程序起点
		System.out.println("大家一起开始去打饭\r\n");
		
		for (int i = 0; i < list.size(); i++) {
			new Thread(new HaveLunchTask(cyclicBarrier), list.get(i)).start();
		}
	}

}

4.AbstractQueuedSynchronizer抽象队列同步器(以下简称AQS)

* aqs是java.util.concurrent包下的核心开发组件,用于构建多线程间的锁或其他相关同步器的基础框架,底层基于一个FIFO队列来维护各个线程。

* aqs提供了两种模式:排他模式和共享模式。

   排他模式:其他线程获取锁将无法成功

   共享模式:其他线程获取锁可能会成功

*  aqs是以一个int类型state来维护同步状态,当state为0时表示未占用,可以获取锁,当state为1时表示占用。

   并为该状态提供以下几个API供使用:

   final int getState();

   final void setState(int newState);

   final boolean compareAndSetState(int expect, int update);//检查当前状态,设置新的状态

   final void setExclusiveOwnerThread(Thread thread);//设置独占线程,当释放锁时,应设置为null

*  aqs是一个抽象类,通常使用其实现一个自定义的同步器的时候,都是在自定义同步器的内部实现一个AbstractQueuedSynchronizer的子类,并复写以下方法:

    boolean isHeldExclusively();//是否处于占用状态

    boolean tryAcquire(int arg);//当状态为0时获取锁

    boolean tryRelease(int arg);//释放锁,将状态置为0

    Condition newCondition();//new一个Condition的实现,比如:java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject

*  关于AQS的用法可以参考java.util.concurrent.locks.ReentrantLock该类,或者基于AQS实现一个简单的lock

public class AQSDemo {
	public static void main(String[] args) {
		
		Lock lock = new DefaultLock();
				
		new Thread(() -> {
			while(true) {
				try {
					Thread.sleep(1000);
					lock.lock();
					System.out.print(Thread.currentThread().getName() + "--> print-");
					for(int i = 0; i < 10; i ++) {
						System.out.print(i);
					}
					System.out.println();
				}catch (Exception e) {
					e.printStackTrace();
				}finally {
					lock.unlock();
				}
			}
		}, "producer").start();
		
		new Thread(() -> {
			while(true) {
				try {
					Thread.sleep(1000);
					lock.lock();
					System.out.print(Thread.currentThread().getName() + "--> print-");
					for(int i = 0; i < 10; i ++) {
						System.out.print(i);
					}
					System.out.println();
				}catch (Exception e) {
					e.printStackTrace();
				}finally {
					lock.unlock();
				}
			}
		}, "consumer").start();

	}

	static class DefaultLock implements Lock, java.io.Serializable {

		private static final long serialVersionUID = 2219105556300329463L;
		private final Sync sync = new Sync();

		// 实现AQS的内部类
		private static class Sync extends AbstractQueuedSynchronizer {

			private static final long serialVersionUID = 6931334064340153098L;

			// 返回是否被占用
			protected boolean isHeldExclusively() {
				return getState() == 1;
			}

			// 如果state是0,则获取锁,并立即返回true
			public boolean tryAcquire(int acquires) {
				assert acquires == 1; // Otherwise unused
				if (compareAndSetState(0, 1)) {
					setExclusiveOwnerThread(Thread.currentThread());
					return true;
				}
				return false;
			}

			// 释放锁,并把state置为0,设置exclusiveOwnerThread独占线程为null
			protected boolean tryRelease(int releases) {
				assert releases == 1; // Otherwise unused
				if (getState() == 0)
					throw new IllegalMonitorStateException();
				setExclusiveOwnerThread(null);
				setState(0);
				return true;
			}

			// Provides a Condition
			Condition newCondition() {
				return new ConditionObject();
			}
		}

		@Override
		public void lock() {
			sync.acquire(1);
		}

		@Override
		public void lockInterruptibly() throws InterruptedException {
			sync.acquireInterruptibly(1); // 获取锁,阻塞到线程被中断退出
		}

		@Override
		public boolean tryLock() {
			return sync.tryAcquire(1);
		}

		@Override
		public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
			return sync.tryAcquireNanos(1, unit.convert(time, unit));
		}

		@Override
		public void unlock() {
			sync.release(1);
		}

		@Override
		public Condition newCondition() {
			return sync.newCondition();
		}
	}
}

9.线程池

线程池总结 -> 24.

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值