JAVA JUC线程

6 篇文章 0 订阅
4 篇文章 0 订阅

JAVA JUC线程

JAVA JUC简介

在Java 5.0 提供了 java.util.concurrent (简称JUC )包,在此包中增加了在并发编程中很常用的实用工具类,用于定义类似于线程的自定义子系统,包括线程池、异步 IO 和轻量级任务框架。
提供可调的、灵活的线程池。还提供了设计用于 多线程上下文中的 Collection 实现等。

1、volatile 关键字内存可见性

举例

创建一个java project
在这里插入图片描述

package com.atguigu.juc;
/**
 * 一、volatile关键字
 * 
 * @author john
 *
 */
public class TestVolatile {

	//有两个线程
	public static void main(String[] args) {
		
		//子线程  (作用:就是给flag改值)
		ThreadDemo td = new ThreadDemo();
		new Thread(td).start();
		
		//main线程 (作用:就是读flag的值,只要flag为true,则打印内容结束循环)
		while(true) {
			if(td.isFlag()) {
				System.out.println("------------------");
				System.out.println("flag已经变为了true");
				System.out.println("------------------");
				break;
			}
		}
	}
}

class ThreadDemo implements Runnable{

	//标记
	private boolean flag = false;
	
	@Override
	public void run() {
		//休息200毫秒
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
		}

		flag = true;
		
		System.out.println("flag已经变为了" + isFlag());
		
	}

	public boolean isFlag() {
		return flag;
	}

	public void setFlag(boolean flag) {
		this.flag = flag;
	}
}

接下来我们看看横线是否会被打印,循环是否会结束。
在这里插入图片描述
没有横线,也没有退出循环。
按理说两个线程应该访问的是共享的数据。
这时候我们就要说一个内存可见性的问题。
jvm会为每一个线程都会分配一个独立的缓存用于提高效率。
在这里插入图片描述
main线程中的while(true) 就是调用系统比较底层的代码,执行效率特别高。所以main线程都没有机会到主存中再次读取数据。flag一直都是false。

内存可见性

  • 内存可见性(Memory Visibility)是指当某个线程正在使用对象状态而另一个线程在同时修改该状态,需要确保当一个线程修改了对象状态后,其他线程能够看到发生的状态变化。
  • 可见性错误是指当读操作与写操作在不同的线程中执行时,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情。
  • 我们可以通过同步来保证对象被安全地发布。除此之外我们也可以使用一种更加轻量级的volatile 变量。

如何解决呢

同步锁

同步锁能保证每次都刷新缓存。

package com.atguigu.juc;
/**
 * 一、volatile关键字
 * 
 * @author john
 *
 */
public class TestVolatile {

	//有两个线程
	public static void main(String[] args) {
		
		//子线程  (作用:就是给flag改值)
		ThreadDemo td = new ThreadDemo();
		new Thread(td).start();
		
		//main线程 (作用:就是读flag的值,只要flag为true,则打印内容结束循环)
		while(true) {
			synchronized (td) {
				if(td.isFlag()) {
					System.out.println("------------------");
					System.out.println("------------------");
					break;
				}
			}
		}
	}
}

class ThreadDemo implements Runnable{

	//标记
	private boolean flag = false;
	
	@Override
	public void run() {
		//休息200毫秒
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
		}

		flag = true;
		
		System.out.println("flag已经变为了" + isFlag());
		
	}
	public boolean isFlag() {
		return flag;
	}
	public void setFlag(boolean flag) {
		this.flag = flag;
	}
}

在这里插入图片描述
有横线,也退出循环了。
但是用了同步锁效率就会非常的低。
如果有多个线程访问,但是有一个线程持有了这个锁,这个时候就阻塞了。
这个时候就有了 “主角” volatile关键字

  • volatile关键字:当多个线程进行操作共享数据时,可以保证内存中的数据可见。
package com.atguigu.juc;
/**
 * 一、volatile关键字
 * 
 * @author john
 *
 */
public class TestVolatile {

	//有两个线程
	public static void main(String[] args) {
		
		//子线程  (作用:就是给flag改值)
		ThreadDemo td = new ThreadDemo();
		new Thread(td).start();
		
		//main线程 (作用:就是读flag的值,只要flag为true,则打印内容结束循环)
		while(true) {
				if(td.isFlag()) {
					System.out.println("------------------");
					System.out.println("------------------");
					break;
				}
		}
	}
}

class ThreadDemo implements Runnable{

	//标记
	private volatile boolean flag = false;
	
	@Override
	public void run() {
		//休息200毫秒
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
		}

		flag = true;
		
		System.out.println("flag已经变为了" + isFlag());
		
	}
	public boolean isFlag() {
		return flag;
	}
	public void setFlag(boolean flag) {
		this.flag = flag;
	}
}

在这里插入图片描述
有横线,也退出循环了。
虽然volatile也会降低效率,但是比起同步锁还是要高的。
volatile 相较于 synchronized 是一种较为轻量级的同步策略。

volatile关键字

Java提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。可以将volatile看作一个轻量级的锁,但是又与锁有些不同

  • 对于多线程,volatile 不具备“”互斥性” , 互斥性就是指一个线程拿到了锁,另一个线程就进不来了。
  • volatile 不能保证变量的“原子性操作”

2、原子变量和CAS算法

举例

在这里插入图片描述

package com.atguigu.juc;
/**
 * 一、i++ 的原子性问题:i++的操作实际上分为三个步骤"读-改-写"
 * 			int i =10;
 * 			i = i++;//10
 * 			
 * 			int temp = i;
 * 			i = i + 1;
 * 			i = temp;
 *
 */
public class TestAtomicDemo {
	public static void main(String[] args) {
		AtomicDemo ad = new AtomicDemo();
		//是个线程
		for (int i = 0; i < 10; i++) {
			new Thread(ad).start();
		}
	}
}
class AtomicDemo implements Runnable{

	private volatile int serialNumber = 0;
	
	@Override
	public void run() {
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
		}
		
		System.out.println(Thread.currentThread().getName() + " : " + getSerialNumber());
	}
	
	public int getSerialNumber() {
		return serialNumber++;
	}
}

产生多线程安全问题,原子性问题
在这里插入图片描述
怎么产生的呢?
在这里插入图片描述

原子变量

jdk1.5后java.util.concurrent.atomic 包下提供了常用的原子变量:
在这里插入图片描述

  • 1、其中变量都由 volatile 修饰 ,所以保证了内存可见性。
  • 2、CAS(Compare-And-Swap) 算法保证数据的原子性。
    CAS算法 :是硬件对于并发操作共享数据的支持。
    CAS 包含了三个操作数:
    内存值 V
    预估值 A
    更新值 B
    当且仅当V == A时,V = B。否则,将不做任何操作。

在这里插入图片描述

package com.atguigu.juc;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 一、i++ 的原子性问题:i++的操作实际上分为三个步骤"读-改-写"
 * 			int i =10;
 * 			i = i++;//10
 * 			
 * 			int temp = i;
 * 			i = i + 1;
 * 			i = temp;
 *
 */
public class TestAtomicDemo {
	public static void main(String[] args) {
		AtomicDemo ad = new AtomicDemo();
		//是个线程
		for (int i = 0; i < 10; i++) {
			new Thread(ad).start();
		}
	}
}
class AtomicDemo implements Runnable{

//	private volatile int serialNumber = 0;
	
	private AtomicInteger serialNumber = new AtomicInteger();
	
	@Override
	public void run() {
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
		}
		
		System.out.println(Thread.currentThread().getName() + " : " + getSerialNumber());
	}
	public int getSerialNumber() {
		return serialNumber.getAndIncrement();
	}
}

在这里插入图片描述

模拟CAS算法

package com.atguigu.juc;
/**
 * 模拟CAS算法
 * @author john
 *
 */
public class TestCompareAndSwap {
	
	
	public static void main(String[] args) {
		final CompareAndSwap cas = new CompareAndSwap();
		for (int i = 0; i < 10; i++) {
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					int expectedValue = cas.get();
					boolean b = cas.compareAndSet(expectedValue, (int)(Math.random() * 101));
					System.out.println(b);
				}
			}).start();
		}
	}
	
}

class CompareAndSwap{
	//内存值
	private int value;
	
	//获取内存值
	public synchronized int get() {
		return value;
	}
	
	//比较 
	//oldValue:内存值V ,expectValue:预估值A , newValue:更新值B。
	public synchronized int compareAndSwap(int expectValue,int newValue) {
		int oldValue = value;
		
		if(oldValue == expectValue) {
			this.value = newValue;
		}
		
		return oldValue;	
	}
	
	//设置
	public synchronized boolean compareAndSet(int expectedValue, int newValue) {
		return expectedValue == compareAndSwap(expectedValue, newValue);
	}
}

3、ConcurrentHashMap锁分段机制

  • Java5.0在java.util.concurrent包中提供了多种并发容器类来改进同步容器的性能。
  • ConcurrentHashMap同步容器类是Java5增加的一个线程安全的哈希表。对与多线程的操作,介于HashMap与Hashtable之间。内部采用“”锁分段”机制替代Hashtable的独占锁。进而提高性能。
  • 当期望许多线程访问一个给定collection时,ConcurrentHashMap通常优于同步的HashMap,ConcurrentSkipListMap通常优于同步的TreeMap。当期望的读数和遍历远远大于列表的更新数时,copyOnWriteArrayList优于同步的ArrayList。

在这里插入图片描述
Hashtable 因为添加了锁的机制,不仅效率很低,而且复合操作不安全。
在这里插入图片描述

copyOnWriteArrayList

package com.atguigu.juc;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;



/**
 * 
 * CopyOnWriteArrayList / CopyOnWriteArraySet : "写入并复制"
 */
public class TestCopyOnWriteArrayList {
	
	public static void main(String[] args) {
		HelloThread ht = new HelloThread();
		
		for (int i = 0; i < 10; i++) {
			new Thread(ht).start();
		}
	}
}

class HelloThread implements Runnable{

	private static List<String> list = Collections.synchronizedList(new ArrayList<String>());
	
	static {
		list.add("AA");
		list.add("BB");
		list.add("CC");
	}
	
	@Override
	public void run() {
		
		Iterator<String> it = list.iterator();
		
		while(it.hasNext()) {
			System.out.println(it.next());
			
			list.add("AA");
		}
	}
}

出现并发修改异常。因为迭代和List.add都是访问同一个数据源。
在这里插入图片描述
使用CopyOnWriteArrayList后

package com.atguigu.juc;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;



/**
 * 
 * CopyOnWriteArrayList / CopyOnWriteArraySet : "写入并复制"
 */
public class TestCopyOnWriteArrayList {
	
	public static void main(String[] args) {
		HelloThread ht = new HelloThread();
		
		for (int i = 0; i < 10; i++) {
			new Thread(ht).start();
		}
	}
}

class HelloThread implements Runnable{

//	private static List<String> list = Collections.synchronizedList(new ArrayList<String>());
	
	private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList();
	
	static {
		list.add("AA");
		list.add("BB");
		list.add("CC");
	}
	
	@Override
	public void run() {
		
		Iterator<String> it = list.iterator();
		
		while(it.hasNext()) {
			System.out.println(it.next());
			
			list.add("AA");
		}
	}
}

在这里插入图片描述
CopyOnWriteArrayList在每次写入时,都会复制一个新的List。
所以CopyOnWriteArrayList适合迭代,不适合添加操作。

4、CountDownLatch

  • Java5.0在java.util.concurrent包中提供了多种并发容器类来改进同步容器的性能。
  • CountDownLatch一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
  • 闭锁可以延迟线程的进度直到其到达终止状态,闭锁可以用来确保某些活动直到其他活动都完成才继续执行:
    1.确保某个计算在其需要的所有资源都被初始化之后才继续执行;
    2.确保某个服务在其依赖的所有其他服务都已经启动之后才启动;
    3.等待直到某个操作所有参与者都准备就绪再继续执行。

举个栗子

package com.threadTest;

import java.util.Iterator;

/**
 * 要求:6位同学离开教室之后,班长才能关门离开
 * @author john
 *
 */
public class CountDownLatchDemo {
	public static void main(String[] args) {
		
		for (int i = 1; i<=6; i++) {
			new Thread(() ->  {
				System.out.println(Thread.currentThread().getName()+"\t 上完自习,离开教室");
			},String.valueOf(i)).start();
		}
		
		System.out.println(Thread.currentThread().getName()+"\t*************班长最后关门走人");
		
	}
}

班长直接把6位同学锁在教室里面了
在这里插入图片描述

package com.threadTest;

import java.util.Iterator;
import java.util.concurrent.CountDownLatch;

/**
 * 要求:6位同学离开教室之后,班长才能关门离开
 * @author john
 *
 */
public class CountDownLatchDemo {
	public static void main(String[] args) throws Exception{
		
		//计数6位同学
		CountDownLatch countDownLatch = new CountDownLatch(6);
		
		for (int i = 1; i<=6; i++) {
			new Thread(() ->  {
				System.out.println(Thread.currentThread().getName()+"\t 上完自习,离开教室");
				//每走一位同学减1
				countDownLatch.countDown();
			},String.valueOf(i)).start();
		}
		
		//countDownLatch.await()是等待,直到计数变为0
		countDownLatch.await();
		System.out.println(Thread.currentThread().getName()+"\t*************班长最后关门走人");
		
	}
}

在这里插入图片描述

枚举在实际生产中的案例

package com.enums;


import jdk.nashorn.internal.objects.annotations.Getter;

/**
 * 相当于一个小数据库
 * @author john
 *
 */
public enum CountryEnum {

	ONE(1,"齐"),TWO(2,"楚"),THREE(3,"燕"),FOUR(4,"赵"),FIVE(5,"魏"),SIX(6,"韩");
	
	private Integer retCode;
	private String retMessage;
	
	public Integer getRetCode() {
		return retCode;
	}

	public void setRetCode(Integer retCode) {
		this.retCode = retCode;
	}

	public String getRetMessage() {
		return retMessage;
	}

	public void setRetMessage(String retMessage) {
		this.retMessage = retMessage;
	}

	CountryEnum(Integer retCode, String retMessage) {
		this.retCode = retCode;
		this.retMessage = retMessage;
	}
	
	public static CountryEnum forEach_CountryEnum(int index) {
		CountryEnum[] myArray = CountryEnum.values();
		for (CountryEnum countryEnum : myArray) {
			if(index == countryEnum.getRetCode()) {
				return countryEnum;
			}
		}
		return null;
	}

	
}

package com.threadTest;

import java.util.Iterator;
import java.util.concurrent.CountDownLatch;

import com.enums.CountryEnum;

/**
 * 要求:秦国灭掉其他6个国家,才能一统天下
 * @author john
 *
 */
public class CountDownLatchDemo {
	public static void main(String[] args) throws Exception{
		
		//计数6个国家
		CountDownLatch countDownLatch = new CountDownLatch(6);
		
		for (int i = 1; i<=6; i++) {
			new Thread(() ->  {
				System.out.println(Thread.currentThread().getName()+"\t 国,被灭");
				//每走一位同学减1
				countDownLatch.countDown();
			},CountryEnum.forEach_CountryEnum(i).getRetMessage()).start();
		}
		
		//countDownLatch.await()是等待,直到计数变为0
		countDownLatch.await();
		System.out.println(Thread.currentThread().getName()+"\t*************秦帝国,一统华夏");
		
	}
}

在这里插入图片描述

5、同步锁Lock

package com.atguigu.juc;

/**
 * 一、用于解决多线程安全问题的方式:
 * 
 * synchronized:隐式锁
 * 1. 同步代码块
 * 
 * 2. 同步方法
 * 
 * jdk1.5 后
 * 3. 同步锁Lock
 * 注意:是一个显示锁,需要通过lock()方法上锁,必须通过unlock()方法进行释放锁。
 * 
 *
 */
public class TestLock {

	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		
		new Thread(ticket, "1号窗口").start();
		new Thread(ticket, "2号窗口").start();
		new Thread(ticket, "3号窗口").start();
	}
	
}

/**
 *买票方法
 */
class Ticket implements Runnable{

	//票的数量
	private int tick = 100;
	
	@Override
	public void run() {
		while(true) {
			if(tick > 0) {
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
				}
				System.out.println(Thread.currentThread().getName() + " 完成售票,余票为:" + --tick);
			}else {
				break;
			}
		}

		
	}
	
}

出现线程安全的问题
在这里插入图片描述

package com.atguigu.juc;

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

/**
 * 一、用于解决多线程安全问题的方式:
 * 
 * synchronized:隐式锁
 * 1. 同步代码块
 * 
 * 2. 同步方法
 * 
 * jdk1.5 后
 * 3. 同步锁Lock
 * 注意:是一个显示锁,需要通过lock()方法上锁,必须通过unlock()方法进行释放锁。
 * 
 *
 */
public class TestLock {

	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		
		new Thread(ticket, "1号窗口").start();
		new Thread(ticket, "2号窗口").start();
		new Thread(ticket, "3号窗口").start();
	}
	
}

/**
 *买票方法
 */
class Ticket implements Runnable{

	//票的数量
	private int tick = 100;
	
	private Lock lock = new ReentrantLock();
	
	@Override
	public void run() {
		while(true) {
			
			lock.lock();
			
			try {
				if(tick > 0) {
					try {
						Thread.sleep(200);
					} catch (InterruptedException e) {
					}
					System.out.println(Thread.currentThread().getName() + " 完成售票,余票为:" + --tick);
				}else {
					break;
				}
			}finally {
				lock.unlock();
			}

		}

		
	}
	
}

加入Lock之后
在这里插入图片描述

6、生产者消费者案例

代码示例

package com.atguigu.juc;

/**
 * 生产者和消费者案例
 * @author john
 *
 */
public class TestProductorAndConsumer {
	public static void main(String[] args) {
		Clerk clerk = new Clerk();
		
		Productor productor = new Productor(clerk);
		Consumer consumer = new Consumer(clerk);
		
		new Thread(productor,"生产者 A").start();
		new Thread(consumer,"消费者 B").start();
	}
}

/**
 * 店员
 */
class Clerk{
	private int product = 0;
	
	//进货
	public synchronized void get() {
		if(product >= 10) {
			System.out.println("产品已满!");
		}else {
			System.out.println(Thread.currentThread().getName() + " : " + ++product);
		}
	}
	
	//卖货
	public synchronized void sale() {
		if(product <= 0){
			System.out.println("缺货");
		}else {
			System.out.println(Thread.currentThread().getName() + " : " + --product);
		}
	}
}

//生产者
class Productor implements Runnable{
	
	private Clerk clerk;
	
	public Productor(Clerk clerk) {
		this.clerk = clerk;
	}
	
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			clerk.get();
		}
	}
}

//消费者
class Consumer implements Runnable{
	
	private Clerk clerk;
	
	public Consumer(Clerk clerk) {
		this.clerk = clerk;
	}
	
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			clerk.sale();
		}
	}
}

出现一个问题:生产者在产品已满的情况下,仍然不断进货;消费者在缺货的情况下,仍然不断消费;
在这里插入图片描述
在这里插入图片描述

等待唤醒机制

package com.atguigu.juc;

/**
 * 生产者和消费者案例
 * @author john
 *
 */
public class TestProductorAndConsumer {
	public static void main(String[] args) {
		Clerk clerk = new Clerk();
		
		Productor productor = new Productor(clerk);
		Consumer consumer = new Consumer(clerk);
		
		new Thread(productor,"生产者 A").start();
		new Thread(consumer,"消费者 B").start();
	}
}

/**
 * 店员
 */
class Clerk{
	private int product = 0;
	
	//进货
	public synchronized void get() {
		if(product >= 10) {
			System.out.println("产品已满!");
			//等待消费者通知
			try {
				this.wait();
			} catch (Exception e) {			
			}
		}else {
			System.out.println(Thread.currentThread().getName() + " : " + ++product);
			//通知其他人可以卖货
			this.notifyAll();
		}
	}
	
	//卖货
	public synchronized void sale() {
		if(product <= 0){
			System.out.println("缺货");
			//等待生产者通知
			try {
				this.wait();
			} catch (Exception e) {
			}
		}else {
			System.out.println(Thread.currentThread().getName() + " : " + --product);
			//通知生产者继续生产
			this.notifyAll();
		}
	}
}

//生产者
class Productor implements Runnable{
	
	private Clerk clerk;
	
	public Productor(Clerk clerk) {
		this.clerk = clerk;
	}
	
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			clerk.get();
		}
	}
}

//消费者
class Consumer implements Runnable{
	
	private Clerk clerk;
	
	public Consumer(Clerk clerk) {
		this.clerk = clerk;
	}
	
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			clerk.sale();
		}
	}
}

现在此时都是有效数据
在这里插入图片描述
现在改动一下(生产者延迟200毫秒,库存最大值改为1),放大一个问题

package com.atguigu.juc;

/**
 * 生产者和消费者案例
 * @author john
 *
 */
public class TestProductorAndConsumer {
	public static void main(String[] args) {
		Clerk clerk = new Clerk();
		
		Productor productor = new Productor(clerk);
		Consumer consumer = new Consumer(clerk);
		
		new Thread(productor,"生产者 A").start();
		new Thread(consumer,"消费者 B").start();
	}
}

/**
 * 店员
 */
class Clerk{
	private int product = 0;
	
	//进货
	public synchronized void get() {
		if(product >= 1) {
			System.out.println("产品已满!");
			//等待消费者通知
			try {
				this.wait();
			} catch (Exception e) {			
			}
		}else {
			System.out.println(Thread.currentThread().getName() + " : " + ++product);
			//通知其他人可以卖货
			this.notifyAll();
		}
	}
	
	//卖货
	public synchronized void sale() {
		if(product <= 0){
			System.out.println("缺货");
			//等待生产者通知
			try {
				this.wait();
			} catch (Exception e) {
			}
		}else {
			System.out.println(Thread.currentThread().getName() + " : " + --product);
			//通知生产者继续生产
			this.notifyAll();
		}
	}
}

//生产者
class Productor implements Runnable{
	
	private Clerk clerk;
	
	public Productor(Clerk clerk) {
		this.clerk = clerk;
	}
	
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
			}
			clerk.get();
		}
	}
}

//消费者
class Consumer implements Runnable{
	
	private Clerk clerk;
	
	public Consumer(Clerk clerk) {
		this.clerk = clerk;
	}
	
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			clerk.sale();
		}
	}
}

在这里插入图片描述
程序并没有停止。
因为出现this.wait() 等待没人唤醒,程序停止在此,程序无法结束。
将进货和卖货的else去掉,则解决。

package com.atguigu.juc;

/**
 * 生产者和消费者案例
 * @author john
 *
 */
public class TestProductorAndConsumer {
	public static void main(String[] args) {
		Clerk clerk = new Clerk();
		
		Productor productor = new Productor(clerk);
		Consumer consumer = new Consumer(clerk);
		
		new Thread(productor,"生产者 A").start();
		new Thread(consumer,"消费者 B").start();
	}
}

/**
 * 店员
 */
class Clerk{
	private int product = 0;
	
	//进货
	public synchronized void get() {
		if(product >= 1) {
			System.out.println("产品已满!");
			//等待消费者通知
			try {
				this.wait();
			} catch (Exception e) {			
			}
		}
		System.out.println(Thread.currentThread().getName() + " : " + ++product);
		//通知其他人可以卖货
		this.notifyAll();
	
	}
	
	//卖货
	public synchronized void sale() {
		if(product <= 0){
			System.out.println("缺货");
			//等待生产者通知
			try {
				this.wait();
			} catch (Exception e) {
			}
		}
		System.out.println(Thread.currentThread().getName() + " : " + --product);
		//通知生产者继续生产
		this.notifyAll();
		
	}
}

//生产者
class Productor implements Runnable{
	
	private Clerk clerk;
	
	public Productor(Clerk clerk) {
		this.clerk = clerk;
	}
	
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
			}
			clerk.get();
		}
	}
}

//消费者
class Consumer implements Runnable{
	
	private Clerk clerk;
	
	public Consumer(Clerk clerk) {
		this.clerk = clerk;
	}
	
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			clerk.sale();
		}
	}
}

在这里插入图片描述
如果再增加一个消费者和一个生产者呢?

package com.atguigu.juc;

/**
 * 生产者和消费者案例
 * @author john
 *
 */
public class TestProductorAndConsumer {
	public static void main(String[] args) {
		Clerk clerk = new Clerk();
		
		Productor productor = new Productor(clerk);
		Consumer consumer = new Consumer(clerk);
		
		new Thread(productor,"生产者 A").start();
		new Thread(consumer,"消费者 B").start();
		
		new Thread(productor,"生产者 C").start();
		new Thread(consumer,"消费者D").start();
	}
}

/**
 * 店员
 */
class Clerk{
	private int product = 0;
	
	//进货
	public synchronized void get() {
		if(product >= 1) {
			System.out.println("产品已满!");
			//等待消费者通知
			try {
				this.wait();
			} catch (Exception e) {			
			}
		}
		System.out.println(Thread.currentThread().getName() + " : " + ++product);
		//通知其他人可以卖货
		this.notifyAll();
	
	}
	
	//卖货
	public synchronized void sale() {
		if(product <= 0){
			System.out.println("缺货");
			//等待生产者通知
			try {
				this.wait();
			} catch (Exception e) {
			}
		}
		System.out.println(Thread.currentThread().getName() + " : " + --product);
		//通知生产者继续生产
		this.notifyAll();
		
	}
}

//生产者
class Productor implements Runnable{
	
	private Clerk clerk;
	
	public Productor(Clerk clerk) {
		this.clerk = clerk;
	}
	
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
			}
			clerk.get();
		}
	}
}

//消费者
class Consumer implements Runnable{
	
	private Clerk clerk;
	
	public Consumer(Clerk clerk) {
		this.clerk = clerk;
	}
	
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			clerk.sale();
		}
	}
}

明显出现问题,出现虚假唤醒。

在这里插入图片描述

虚假唤醒

我们只需要把if改为while

package com.atguigu.juc;

/**
 * 生产者和消费者案例
 * @author john
 *
 */
public class TestProductorAndConsumer {
	public static void main(String[] args) {
		Clerk clerk = new Clerk();
		
		Productor productor = new Productor(clerk);
		Consumer consumer = new Consumer(clerk);
		
		new Thread(productor,"生产者 A").start();
		new Thread(consumer,"消费者 B").start();
		
		new Thread(productor,"生产者 C").start();
		new Thread(consumer,"消费者D").start();
	}
}

/**
 * 店员
 */
class Clerk{
	private int product = 0;
	
	//进货
	public synchronized void get() {
		while(product >= 1) {
			System.out.println("产品已满!");
			//等待消费者通知
			try {
				this.wait();
			} catch (Exception e) {			
			}
		}
		System.out.println(Thread.currentThread().getName() + " : " + ++product);
		//通知其他人可以卖货
		this.notifyAll();
	
	}
	
	//卖货
	public synchronized void sale() {
		while(product <= 0){
			System.out.println("缺货");
			//等待生产者通知
			try {
				this.wait();
			} catch (Exception e) {
			}
		}
		System.out.println(Thread.currentThread().getName() + " : " + --product);
		//通知生产者继续生产
		this.notifyAll();
		
	}
}

//生产者
class Productor implements Runnable{
	
	private Clerk clerk;
	
	public Productor(Clerk clerk) {
		this.clerk = clerk;
	}
	
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
			}
			clerk.get();
		}
	}
}

//消费者
class Consumer implements Runnable{
	
	private Clerk clerk;
	
	public Consumer(Clerk clerk) {
		this.clerk = clerk;
	}
	
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			clerk.sale();
		}
	}
}

解决~
为了避免虚假唤醒问题,应该总是使用在循环中
在这里插入图片描述

使用Lock替换synchronized

Condition
  • Condition接口描述了可能会与锁有关联的条件变量。这些变量在用法上与使用Object.wait访问的隐式监视器类似,但提供了更强大的功能。需要特别指出的是,单个Lock可能与多个Condition对象关联。为了避免兼容性问题,Condition方法的名称与对应的Object版本中的不同。
  • 在Condition对象中,与wait、notify和notifyAll方法对应的分别是await、signal和signalAll。
  • Condition实例实质上被绑定到一个锁上。要为特定Lock实例获得Condition实例,清使用newCondition()方法。
package com.atguigu.juc;

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

/**
 * 生产者和消费者案例
 * @author john
 *
 */
public class TestProductorAndConsumerForLock {
	public static void main(String[] args) {
		Clerk clerk = new Clerk();
		
		Productor productor = new Productor(clerk);
		Consumer consumer = new Consumer(clerk);
		
		new Thread(productor,"生产者 A").start();
		new Thread(consumer,"消费者 B").start();
		
		new Thread(productor,"生产者 C").start();
		new Thread(consumer,"消费者D").start();
	}
}

/**
 * 店员
 */
class Clerk{
	private int product = 0;
	
	private Lock lock = new ReentrantLock();
	
	private Condition condition = lock.newCondition();
	
	//进货
	public void get() {
		
		lock.lock();
		
		try {
			while(product >= 1) {
				System.out.println("产品已满!");
				//等待消费者通知
				try {
					condition.await();
				} catch (Exception e) {			
				}
			}
			System.out.println(Thread.currentThread().getName() + " : " + ++product);
			//通知其他人可以卖货
			condition.signalAll();
		} finally {
			lock.unlock();
		}
		

	
	}
	
	//卖货
	public void sale() {
		
		lock.lock();
		
		try {
			while(product <= 0){
				System.out.println("缺货");
				//等待生产者通知
				try {
					condition.await();
				} catch (Exception e) {
				}
			}
			System.out.println(Thread.currentThread().getName() + " : " + --product);
			//通知生产者继续生产
			condition.signalAll();
		} finally {
			lock.unlock();
		}
		
		
		
	}
}

//生产者
class Productor implements Runnable{
	
	private Clerk clerk;
	
	public Productor(Clerk clerk) {
		this.clerk = clerk;
	}
	
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
			}
			clerk.get();
		}
	}
}

//消费者
class Consumer implements Runnable{
	
	private Clerk clerk;
	
	public Consumer(Clerk clerk) {
		this.clerk = clerk;
	}
	
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			clerk.sale();
		}
	}
}

7、线程按序交替案例

  • 编写一个程序,开启3个线程,这三个线程的ID分别为A、B、C ,每个线程将自己的ID在屏幕上打印20遍, 要求输出的结果必须按顺序显示。
    如: ABCABCABC…依次递归
package com.atguigu.juc;

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

/*
 * 编写一个程序,开启 3 个线程,这三个线程的 ID 分别为 A、B、C,每个线程将自己的 ID 在屏幕上打印 10 遍,要求输出的结果必须按顺序显示。
 *	如:ABCABCABC…… 依次递归
 */
public class TestABCAlternate {
	
	public static void main(String[] args) {
		AlternateDemo ad = new AlternateDemo();
		//线程A
		new Thread(new Runnable() {
			@Override
			public void run() {
				
				for (int i = 1; i <= 20; i++) {
					ad.loopA(i);
				}
				
			}
		}, "A").start();
		//线程B
		new Thread(new Runnable() {
			@Override
			public void run() {
				
				for (int i = 1; i <= 20; i++) {
					ad.loopB(i);
				}
				
			}
		}, "B").start();
		//线程C
		new Thread(new Runnable() {
			@Override
			public void run() {
				
				for (int i = 1; i <= 20; i++) {
					ad.loopC(i);
					
					System.out.println("------------------第"+ i +"轮-----------------");
				}
				
			}
		}, "C").start();
	}

}

class AlternateDemo{
	
	private int number = 1; //当前正在执行线程的标记
	
	private Lock lock = new ReentrantLock();
	private Condition condition1 = lock.newCondition();
	private Condition condition2 = lock.newCondition();
	private Condition condition3 = lock.newCondition();
	
	/**
	 * @param totalLoop : 循环第几轮
	 */
	public void loopA(int totalLoop){
		lock.lock();
		
		try {
			//1. 判断
			if(number != 1){
				condition1.await();
			}
			
			//2. 打印
			for (int i = 1; i <= 1; i++) {
				System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
			}
			
			//3. 唤醒
			number = 2;
			condition2.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void loopB(int totalLoop){
		lock.lock();
		
		try {
			//1. 判断
			if(number != 2){
				condition2.await();
			}
			
			//2. 打印
			for (int i = 1; i <= 1; i++) {
				System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
			}
			
			//3. 唤醒
			number = 3;
			condition3.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void loopC(int totalLoop){
		lock.lock();
		
		try {
			//1. 判断
			if(number != 3){
				condition3.await();
			}
			
			//2. 打印
			for (int i = 1; i <= 1; i++) {
				System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
			}
			
			//3. 唤醒
			number = 1;
			condition1.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
}

8、ReadWriteLock读写锁

  • ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有writer , 读取锁可以由多个reader线程同时保持。写入锁是独占的。。
  • ReadWriteLock 读取操作通常不会改变共享资源, 但执行写入操作时,必须独占方式来获取锁。对于读取操作占多数的数据结构。ReadWriteLock 能提供比独占锁更高的并发性。 而对于只读的数据结构,其中包含的不变性可以完全不需要考虑加锁操作。
package com.atguigu.juc;

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

/*
 * 1. ReadWriteLock : 读写锁
 * 
 * 写写/读写 需要“互斥”
 * 读读 不需要互斥
 * 
 */
public class TestReadWriteLock {

	public static void main(String[] args) {
		ReadWriteLockDemo rw = new ReadWriteLockDemo();
		//一个写线程
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				rw.set((int)(Math.random() * 101));
			}
		}, "Write:").start();
		
		//一百个读线程
		for (int i = 0; i < 100; i++) {
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					rw.get();
				}
			}).start();
		}
	}
	
}

class ReadWriteLockDemo{
	
	private int number = 0;
	
	private ReadWriteLock lock = new ReentrantReadWriteLock();
	
	//读
	public void get(){
		lock.readLock().lock(); //上锁
		
		try{
			System.out.println(Thread.currentThread().getName() + " : " + number);
		}finally{
			lock.readLock().unlock(); //释放锁
		}
	}
	
	//写
	public void set(int number){
		lock.writeLock().lock();
		
		try{
			System.out.println(Thread.currentThread().getName());
			this.number = number;
		}finally{
			lock.writeLock().unlock();
		}
	}
}

线程八锁

代码示例

题目:判断打印的“one” or “two” ?

1. 两个普通同步方法,两个线程,标准打印
package com.atguigu.juc;

/**
 * 题目:判断打印的“one” or "two" ?
 *	1.两个普通同步方法,两个线程,标准打印, 打印: // one two
 *
 */
public class TestThread8Monitor {

	public static void main(String[] args) {
		Number number = new Number();
		new Thread(new Runnable() {

			@Override
			public void run() {
				number.getOne();
			}
			
		}).start();
		
		new Thread(new Runnable() {

			@Override
			public void run() {
				number.getTwo();
			}
			
		}).start();
	}
}
class Number{
	public synchronized void getOne() {
		System.out.println("one");
	}
	
	public synchronized void getTwo() {
		System.out.println("two");
	}
}

在这里插入图片描述

2. 新增Thread.sleep() 给getOne()
package com.atguigu.juc;

/**
 * 题目:判断打印的“one” or "two" ?
 *	1.两个普通同步方法,两个线程,标准打印。打印: // one two
 *	2.新增Thread.sleep() 给getOne()。 打印: // one two
 *	
 *
 */
public class TestThread8Monitor {

	public static void main(String[] args) {
		Number number = new Number();
		new Thread(new Runnable() {

			@Override
			public void run() {
				number.getOne();
			}
			
		}).start();
		
		new Thread(new Runnable() {

			@Override
			public void run() {
				number.getTwo();
			}
			
		}).start();
	}
}
class Number{
	public synchronized void getOne() {
		//睡眠3秒
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
		}
		
		System.out.println("one");
	}
	
	public synchronized void getTwo() {
		System.out.println("two");
	}
}

在这里插入图片描述

3. 新增普通方法getThree()
package com.atguigu.juc;

/**
 * 题目:判断打印的“one” or "two" ?
 *	1.两个普通同步方法,两个线程,标准打印。打印: // one two
 *	2.新增Thread.sleep() 给getOne()。 打印: // one two
 *	3.新增普通方法getThree() 。  打印: // three one two
 *
 */
public class TestThread8Monitor {

	public static void main(String[] args) {
		Number number = new Number();
		new Thread(new Runnable() {

			@Override
			public void run() {
				number.getOne();
			}
			
		}).start();
		
		new Thread(new Runnable() {

			@Override
			public void run() {
				number.getTwo();
			}
			
		}).start();
		
		new Thread(new Runnable() {

			@Override
			public void run() {
				number.getThree();
			}
			
		}).start();
	}
}
class Number{
	public synchronized void getOne() {
		//睡眠3秒
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
		}
		
		System.out.println("one");
	}
	
	public synchronized void getTwo() {
		System.out.println("two");
	}
	
	public void getThree() {
		System.out.println("three");
	}
}

在这里插入图片描述

4.两个普通同步方法,两个Number对象
package com.atguigu.juc;

/**
 * 题目:判断打印的“one” or "two" ?
 *	1.两个普通同步方法,两个线程,标准打印。打印: // one two
 *	2.新增Thread.sleep() 给getOne()。 打印: // one two
 *	3.新增普通方法getThree() 。  打印: // three one two
 *	4.两个普通同步方法,两个Number对象,打印: // two one
 *	
 */
public class TestThread8Monitor {

	public static void main(String[] args) {
		Number number = new Number();
		Number number2 = new Number();
		
		new Thread(new Runnable() {

			@Override
			public void run() {
				number.getOne();
			}
			
		}).start();
		
		new Thread(new Runnable() {

			@Override
			public void run() {
				number.getTwo();
			}
			
		}).start();
		
//		new Thread(new Runnable() {
//
//			@Override
//			public void run() {
//				number.getThree();
//			}
//			
//		}).start();
	}
}
class Number{
	public synchronized void getOne() {
		//睡眠3秒
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
		}
		
		System.out.println("one");
	}
	
	public synchronized void getTwo() {
		System.out.println("two");
	}
	
	public void getThree() {
		System.out.println("three");
	}
}

在这里插入图片描述

5.两个普通同步方法,一个Number对象,修改getOne()为静态同步方法
package com.atguigu.juc;

/**
 * 题目:判断打印的“one” or "two" ?
 *	1.两个普通同步方法,两个线程,标准打印。打印: // one two
 *	2.新增Thread.sleep() 给getOne()。 打印: // one two
 *	3.新增普通方法getThree() 。  打印: // three one two
 *	4.两个普通同步方法,两个Number对象,打印: // two one
 *	5.两个普通同步方法,一个Number对象,修改getOne()为静态同步方法,打印: // two one
 */
public class TestThread8Monitor {

	public static void main(String[] args) {
		Number number = new Number();
		Number number2 = new Number();
		
		new Thread(new Runnable() {

			@Override
			public void run() {
				number.getOne();
			}
			
		}).start();
		
		new Thread(new Runnable() {

			@Override
			public void run() {
				number.getTwo();
			}
			
		}).start();
		
//		new Thread(new Runnable() {
//
//			@Override
//			public void run() {
//				number.getThree();
//			}
//			
//		}).start();
	}
}
class Number{
	public static synchronized void getOne() {
		//睡眠3秒
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
		}
		
		System.out.println("one");
	}
	
	public synchronized void getTwo() {
		System.out.println("two");
	}
	
	public void getThree() {
		System.out.println("three");
	}
}
6.两个普通同步方法都为静态同步方法,一个Number对象
package com.atguigu.juc;

/**
 * 题目:判断打印的“one” or "two" ?
 *	1.两个普通同步方法,两个线程,标准打印。打印: // one two
 *	2.新增Thread.sleep() 给getOne()。 打印: // one two
 *	3.新增普通方法getThree() 。  打印: // three one two
 *	4.两个普通同步方法,两个Number对象。打印: // two one
 *	5.两个普通同步方法,一个Number对象,修改getOne()为静态同步方法。打印: // two one
 *	6.两个普通同步方法都为静态同步方法,一个Number对象 。打印: // one two
 *	
 */
public class TestThread8Monitor {

	public static void main(String[] args) {
		Number number = new Number();
		Number number2 = new Number();
		
		new Thread(new Runnable() {

			@Override
			public void run() {
				number.getOne();
			}
			
		}).start();
		
		new Thread(new Runnable() {

			@Override
			public void run() {
				number.getTwo();
			}
			
		}).start();
		
//		new Thread(new Runnable() {
//
//			@Override
//			public void run() {
//				number.getThree();
//			}
//			
//		}).start();
	}
}
class Number{
	public static synchronized void getOne() {
		//睡眠3秒
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
		}
		
		System.out.println("one");
	}
	
	public static synchronized void getTwo() {
		System.out.println("two");
	}
	
	public void getThree() {
		System.out.println("three");
	}
}

在这里插入图片描述

getOne()为静态同步方法,getTwo()为普通同步方法,两个Number对象
package com.atguigu.juc;

/**
 * 题目:判断打印的“one” or "two" ?
 *	1.两个普通同步方法,两个线程,标准打印。打印: // one two
 *	2.新增Thread.sleep() 给getOne()。 打印: // one two
 *	3.新增普通方法getThree() 。  打印: // three one two
 *	4.两个普通同步方法,两个Number对象。打印: // two one
 *	5.两个普通同步方法,一个Number对象,修改getOne()为静态同步方法。打印: // two one
 *	6.两个普通同步方法都为静态同步方法,一个Number对象 。打印: // one two
 *	7.getOne()为静态同步方法,getTwo()为普通同步方法,两个Number对象。打印: two one
 */
public class TestThread8Monitor {

	public static void main(String[] args) {
		Number number = new Number();
		Number number2 = new Number();
		
		new Thread(new Runnable() {

			@Override
			public void run() {
				number.getOne();
			}
			
		}).start();
		
		new Thread(new Runnable() {

			@Override
			public void run() {
				number2.getTwo();
			}
			
		}).start();
		
//		new Thread(new Runnable() {
//
//			@Override
//			public void run() {
//				number.getThree();
//			}
//			
//		}).start();
	}
}
class Number{
	public static synchronized void getOne() {
		//睡眠3秒
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
		}
		
		System.out.println("one");
	}
	
	public synchronized void getTwo() {
		System.out.println("two");
	}
	
	public void getThree() {
		System.out.println("three");
	}
}

在这里插入图片描述

两个普通同步方法都为静态同步方法,两个Number对象
package com.atguigu.juc;

/**
 * 题目:判断打印的“one” or "two" ?
 *	1.两个普通同步方法,两个线程,标准打印。打印: // one two
 *	2.新增Thread.sleep() 给getOne()。 打印: // one two
 *	3.新增普通方法getThree() 。  打印: // three one two
 *	4.两个普通同步方法,两个Number对象。打印: // two one
 *	5.两个普通同步方法,一个Number对象,修改getOne()为静态同步方法。打印: // two one
 *	6.两个普通同步方法都为静态同步方法,一个Number对象 。打印: // one two
 *	7.getOne()为静态同步方法,getTwo()为普通同步方法,两个Number对象。打印:// two one
 *	8.两个普通同步方法都为静态同步方法,两个Number对象 。打印:// one two
 */
public class TestThread8Monitor {

	public static void main(String[] args) {
		Number number = new Number();
		Number number2 = new Number();
		
		new Thread(new Runnable() {

			@Override
			public void run() {
				number.getOne();
			}
			
		}).start();
		
		new Thread(new Runnable() {

			@Override
			public void run() {
				number2.getTwo();
			}
			
		}).start();
		
//		new Thread(new Runnable() {
//
//			@Override
//			public void run() {
//				number.getThree();
//			}
//			
//		}).start();
	}
}
class Number{
	public static synchronized void getOne() {
		//睡眠3秒
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
		}
		
		System.out.println("one");
	}
	
	public static synchronized void getTwo() {
		System.out.println("two");
	}
	
	public void getThree() {
		System.out.println("three");
	}
}

在这里插入图片描述

线程八锁的关键
  1. 非静态方法的锁默认为this, 静态方法的锁为 对应的Class实例。
  2. 某一个时刻内,只能有一个线程持有锁,无论有几个方法
    在这里插入图片描述

9、CyclicBarrier集齐7颗龙珠才能召唤神龙

集齐7颗龙珠才能召唤神龙
7个线程都准备好了,才能开始执行。

package com.threadTest;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * 要求:集齐7颗龙珠才能召唤神龙
 * @author john
 *
 */
public class CyclicBarrierDemo {
	public static void main(String[] args) {
		//CyclicBarrier(int parties,Runnable barrierAction)
		CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()-> {System.out.println("***召唤神龙");});
		
		for (int i = 1; i <= 7; i++) {
			final int tempInt = i;
			new Thread(()-> {
				System.out.println(Thread.currentThread().getName()+"\t 收集到第:"+tempInt+"龙珠");
				try {
					//等待,直到收集到7颗龙珠
					cyclicBarrier.await();
				} catch (InterruptedException | BrokenBarrierException e) {
					e.printStackTrace();
				}
			},String.valueOf(tempInt)).start();			
		}
	}
}

在这里插入图片描述

10、Semaphore 争车位

信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。

package com.threadTest;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreDemo {
	public static void main(String[] args) {
		//模拟3个停车位
		Semaphore semaphore = new Semaphore(3);
		
		//模拟6部汽车
		for (int i = 1; i <= 6; i++) {
			new Thread(()->{
				try {
					semaphore.acquire();
					System.out.println(Thread.currentThread().getName()+"\t抢到车位");
					//停3秒
					try {TimeUnit.SECONDS.sleep(3);}catch(InterruptedException e) {e.printStackTrace();}
					System.out.println(Thread.currentThread().getName()+"\t停车3秒后离开车位");

				} catch (InterruptedException e) {
					e.printStackTrace();
				}finally {
					//释放停车位
					semaphore.release();
				}
			},String.valueOf(i)).start();
		}
		
		
	}
}

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值