Volatile关键字

1.volatile解决可见性

//创建一个类继承Thread类
public class MyThread extends Thread{//定义一个共享的静态成员变量,供多个线程一起使用//给共享的成员变量,添加一个volatile关键字修饰public static volatile int a = 0;//重写Thread类中的run方法,设置线程任务@Overridepublic void run() {System.out.println("Thread-0线程开始执行了,先睡眠2秒钟");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread-0线程睡醒了,改变a的值为1");//a变量被volatile修饰,当改变了变量a的值,volatile关键字会让所有a的变量副本失效,线程想要使用a变的值,需要在静态区中重新获取a=1;System.out.println("Thread-0线程的线程任务执行完毕!");}
}public class Demo01Visible {public static void main(String[] args) {//创建Thread类的子类对象MyThread mt = new MyThread();//调用继续自Thread类中的start方法,开启新的线程执行run方法mt.start();System.out.println("main线程,会继续执行main方法中的代码");while (true){if(MyThread.a==1){System.out.println("main线程,判断变量a的值为1,结束死循环!");break;}}}
}


2.volatile解决有序性

3.volatile不能解决原子性

原子性的解决

概述: 所谓的原子性是指在一次操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行,

多个操作是一个不可以分割的整体(原子)。

比如:从张三的账户给李四的账户转1000元,这个动作将包含两个基本的操作:从张三的账户扣除1000元,给李四的账户增加1000元。这两个操作必须符合原子性的要求,

要么都成功要么都失败

原子类

原子类概述: java从JDK1.5开始提供了java.util.concurrent.atomic包(简称Atomic包),这个包中的原子操作类提供了一种用法简单,性能高效,线程安全地更新一个变量的方式。

1). java.util.concurrent.atomic.AtomicInteger:对int变量进行原子操作的类。

2). java.util.concurrent.atomic.AtomicLong:对long变量进行原子操作的类。

3). java.util.concurrent.atomic.AtomicBoolean:对boolean变量进行原子操作的类。

这些类可以保证对“某种类型的变量”原子操作,多线程、高并发的环境下,就可以保证对变量访问的有序性,从而保证最终的结果是正确的。

AtomicInteger原子型Integer,可以实现原子更新操作

构造方法:
public AtomicInteger():                  初始化一个默认值为0的原子型Integer
public AtomicInteger(int initialValue): 初始化一个指定值的原子型Integer
成员方法:
int get():                               获取值
int getAndIncrement():                   以原子方式将当前值加1,注意,这里返回的是自增前的值。
int incrementAndGet():                   以原子方式将当前值加1,注意,这里返回的是自增后的值。
int addAndGet(int data):                 以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果。
int getAndSet(int value):                以原子方式设置为newValue的值,并返回旧值

2.AtomicInteger类的基本使用

/*java.util.concurrent.atomic.AtomicInteger:对int变量进行原子操作的类构造方法:public AtomicInteger():                 初始化一个默认值为0的原子型Integerpublic AtomicInteger(int initialValue): 初始化一个指定值的原子型Integer成员方法:int get():                               获取值int getAndIncrement():                   以原子方式将当前值加1,注意,这里返回的是自增前的值。int incrementAndGet():                     以原子方式将当前值加1,注意,这里返回的是自增后的值。int addAndGet(int data):               以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果。int getAndSet(int value):              以原子方式设置为newValue的值,并返回旧值。*/
public class Demo01AtomicInteger {public static void main(String[] args) {//创建AtomicInteger对象,初始值为10AtomicInteger ai = new AtomicInteger(10);//int get():  获取值int i = ai.get();System.out.println(i);//10//int getAndIncrement():  以原子方式将当前值加1,注意,这里返回的是自增前的值。int i2 = ai.getAndIncrement();  //i++;System.out.println("i2:"+i2);//i2:10System.out.println(ai);//11//int incrementAndGet(): 以原子方式将当前值加1,注意,这里返回的是自增后的值。int i3 = ai.incrementAndGet();// ++iSystem.out.println("i3:"+i3);//i3:12System.out.println(ai);//12//int addAndGet(int data):以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果。int i4 = ai.addAndGet(100);// 12+100System.out.println("i4:"+i4);//i4:112System.out.println(ai);//112//int getAndSet(int value): 以原子方式设置为newValue的值,并返回旧值。int i5 = ai.getAndSet(88);System.out.println("i5:"+i5);//i5:112 被替换的值System.out.println(ai);//88}
}

AtomicInteger解决变量的可见性、有序性、原子性(重点)

public class MyThread extends Thread{//不要直接使用int类型的变量,使用AtomicIntegerpublic static AtomicInteger money = new AtomicInteger(0);@Overridepublic void run() {System.out.println("Thread-0线程开始改变money的值!");for (int i = 0; i < 10000; i++) {//money.getAndIncrement();//先获取后自增  money++money.incrementAndGet();//先自增后获取  ++money}System.out.println("Thread-0线程执行完毕!");}
}public class Demo01Thread {public static void main(String[] args) throws InterruptedException {//开启新的线程MyThread mt  = new MyThread();mt.start();System.out.println("main继续往下执行");System.out.println("main线程开始改变money的值!");for (int i = 0; i < 10000; i++) {MyThread.money.incrementAndGet();}System.out.println("main线程暂停1秒,让Thread-0执行完毕!");Thread.sleep(1000);System.out.println("最终money为:"+ MyThread.money);}
}

3.AtomicInteger的原理_CAS机制(乐观锁)

第二章 线程安全

AtomicInteger可以对“int类型的变量”做原子操作。但如果需要将“很多行代码”一起作为“原子性”执行——一个线程进入后,必须将所有代码行执行完毕,其它线程才能进入,可以使用synchronized关键字——重量级的同步关键字。

AtomicInteger:只能解决一个变量的原子性

synchronized:可以解决一段代码的原子性

2.线程安全问题的代码实现

/*卖票案例*/
public class RunnableImpl implements Runnable{//定义共享的票源private int ticket = 100;//线程任务:卖票@Overridepublic void run() {//让卖票重复执行while (true){//票大于0,就进行卖票if(ticket>0){//让线程睡眠10毫秒,提供安全问题出现的几率try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票!");ticket--;}}}
}/*创建3个线程进行买相同的100张票*/
public class Demo01PayTticket {public static void main(String[] args) {RunnableImpl r = new RunnableImpl();Thread t0 = new Thread(r);Thread t1 = new Thread(r);Thread t2 = new Thread(r);t0.start();t1.start();t2.start();}
}

4.解决线程安全问题的第一种方式使用同步代码块(重点)

/*卖票案例出现了线程安全问题:1.卖出了重复的票2.卖出了不存在的票解决线程安全问题的第一种方式:使用同步代码块格式:synchronized(锁对象){访问了共享数据的代码(产生了线程安全问题的代码)}注意:锁对象可以是任意的对象new Person()  new Object()  new Student()  "aaa"==>字符数组必须保证所有的线程使用的都是同一个锁对象(唯一)*/
public class RunnableImpl implements Runnable {//定义多个线程访问的共享资源private int ticket = 100;//定义一个锁对象//Object obj = new Object();String obj = "abc";// new char[]{'a','b','c'}//线程任务:卖票@Overridepublic void run() {//让卖票重复执行while (true){//同步代码synchronized (obj){//判断票大于0,就进行卖票if(ticket>0){//让线程睡眠10毫秒,提高线程安全问题出现几率try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票!");ticket--;}}}}
}/*创建3个线程进行买相同的100张票*/
public class Demo01PayTticket {public static void main(String[] args) {RunnableImpl r = new RunnableImpl();Thread t0 = new Thread(r);Thread t1 = new Thread(r);Thread t2 = new Thread(r);t0.start();t1.start();t2.start();}
}

5.同步原理

6.解决线程安全问题的第二种方式——同步方法

/*卖票案例出现了线程安全问题:1.卖出了重复的票2.卖出了不存在的票解决线程安全问题的第二种方式:使用同步方法原理:把访问了共享数据的代码,提取出来,放到一个方法中在方法上添加一个同步关键字synchronized底层也是使用了一个锁对象,把方法锁住,只让一个线程进入到方法中执行格式:修饰符 synchronized 返回值类型 方法名(参数列表){访问了共享数据的代码(产生了线程安全问题的代码)}*/
public class RunnableImpl implements Runnable {//定义多个线程访问的共享资源private static int ticket = 100;//线程任务:卖票@Overridepublic void run() {System.out.println("this:"+this);//this:com.llz.demo06synchronized.RunnableImpl@4554617c//让卖票重复执行while (true){payTicketStatic();}}/*定义一个静态的同步方法静态的同步方法锁对象是谁? 是this吗?不是this,静态优先于对象加载到内存中使用的是本类的class文件对象:RunnbaleImpl.class(反射)*/public static /*synchronized*/ void payTicketStatic(){synchronized (RunnableImpl.class){//判断票大于0,就进行卖票if(ticket>0){//让线程睡眠10毫秒,提高线程安全问题出现几率try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票!");ticket--;}}}/*定义一个同步方法:把代码抽取到一个方法中  ctrl+alt+m同步方法的锁对象是谁?使用的就是根据本类创建的对象this  new RunableImpl();*/public /*synchronized*/ void payTicket() {synchronized (this){//判断票大于0,就进行卖票if(ticket>0){//让线程睡眠10毫秒,提高线程安全问题出现几率try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票!");ticket--;}}}public synchronized void payTicket() {//判断票大于0,就进行卖票if(ticket>0){//让线程睡眠10毫秒,提高线程安全问题出现几率try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票!");ticket--;}}
}/*创建3个线程进行买相同的100张票*/
public class Demo01PayTticket {public static void main(String[] args) {RunnableImpl r = new RunnableImpl();System.out.println("r:"+r);//r:com.llz.demo12synchronized.RunnableImpl@4554617cThread t0 = new Thread(r);Thread t1 = new Thread(r);Thread t2 = new Thread(r);t0.start();t1.start();t2.start();}
}

7.解决线程安全的第三种方式——Lock锁

/*卖票案例出现了线程安全问题:1.卖出了重复的票2.卖出了不存在的票解决线程安全问题的第三种方式:使用Lock锁java.util.concurrent.locks.Lock接口JDK1.5之后出现的Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。接口中的方法:void lock()获取锁。void unlock() 释放锁。java.util.concurrent.locks.ReentrantLock类 implements Lock接口实现步骤:1.在成员位置创建ReentrantLock对象2.在可能出现线程安全问题的代码前,使用lock方法获取锁对象3.在可能出现线程安全问题的代码后,使用unlock方法释放锁对象*/
public class RunnableImpl implements Runnable {//定义多个线程访问的共享资源private int ticket = 100;//1.在成员位置创建ReentrantLock对象ReentrantLock lock = new ReentrantLock();//线程任务:卖票@Overridepublic void run() {//让卖票重复执行while (true){//2.在可能出现线程安全问题的代码前,使用lock方法获取锁对象lock.lock();//默认使用的锁对象也是this//判断票大于0,就进行卖票if(ticket>0){try {//让线程睡眠10毫秒,提高线程安全问题出现几率Thread.sleep(10);System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票!");ticket--;} catch (InterruptedException e) {e.printStackTrace();} finally {//3.在可能出现线程安全问题的代码后,使用unlock方法释放锁对象lock.unlock();//无论是否出现了异常,都会把锁对象释放掉}}}}/*@Overridepublic void run() {//让卖票重复执行while (true){//2.在可能出现线程安全问题的代码前,使用lock方法获取锁对象lock.lock();//默认使用的锁对象也是this//判断票大于0,就进行卖票if(ticket>0){//让线程睡眠10毫秒,提高线程安全问题出现几率try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票!");ticket--;}//3.在可能出现线程安全问题的代码后,使用unlock方法释放锁对象lock.unlock();}}*/
}

8.CAS和Synchronized

CAS和Synchronized都可以保证多线程环境下共享数据的安全性。那么他们两者有什么区别?

Synchronized是从悲观的角度出发:

总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁

共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。因此Synchronized我们也将其称之为悲观锁。jdk中的ReentrantLock也是一种悲观锁。

CAS是从乐观的角度出发:

总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。

CAS这种机制我们也可以将其称之为乐观锁。

并发包

在JDK的并发包java.util.concurrent里提供了几个非常有用的并发容器和并发工具类。供我们在多线程开发中进行使用。这些集合和工具类都可以保证高并发的线程安全问题.

1.并发List集合CopyOnWriteArrayList(重点)

1).java.util.concurrent.CopyOnWriteArrayList(类):它是一个“线程安全”的ArrayList,而java.utils.ArrayList不是线程安全的。
2).如果是多个线程,并发访问同一个ArrayList,我们要使用:CopyOnWriteArrayList

/*java.util.concurrent.CopyOnWriteArrayList<E>:是一个高并发安全的集合*/
public class MyThread extends Thread{//创建一个被多个线程共享的静态ArrayList集合//public static ArrayList<Integer> list = new ArrayList<>();//多线程不安全(1.集合索引越界异常2.数据不准确2000-->1998)public static CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();//多线程安全@Overridepublic void run() {for (int i = 0; i < 1000; i++) {list.add(i);//往集合中添加元素}System.out.println("Thread-0线程往集合中添加元素结束!");}
}public class Demo01CopyOnWriteArrayList {public static void main(String[] args) throws InterruptedException {//创建新线程对象并开启MyThread mt = new MyThread();mt.start();System.out.println("main线程继续执行");for (int i = 0; i < 1000; i++) {MyThread.list.add(i);}System.out.println("main线程休息1秒");Thread.sleep(1000);System.out.println("最终集合的长度为:"+MyThread.list.size());}
}

ArrayList集合的并发问题:

main线程会继续执行,往集合中添加元素
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 73
Thread-0线程开始执行了...往集合中添加元素!
Thread-0线程结束执行了!at java.util.ArrayList.add(ArrayList.java:463)at com.llz.demo12CopyOnWriteArrayList.Demo01.main(Demo01.java:12)main线程往集合中添加元素
main线程休息1秒钟,让Thread-0线程先执行完毕
Thread-0线程往集合中添加元素结束!
最终集合的长度为:1998main线程会继续执行,往集合中添加元素
main线程添加数据结束,等待1秒钟,等待Thread-0线程执行完毕
Thread-0线程开始执行了...往集合中添加元素!
Thread-0线程结束执行了!
ArrayList集合的总长度为:2000

2.并发Set集合CopyOnWriteArraySet(重点)

/*java.util.concurrent.CopyOnWriteArraySet<E>:多线程访问安全的Set集合*/
public class MyThread extends Thread{//创建公共的静态的Set集合,供所有的线程使用//public static HashSet<Integer> set = new HashSet<>();//多线程不安全(数据不准确2000-->1998)public static CopyOnWriteArraySet<Integer> set = new CopyOnWriteArraySet<>();//多线程安全@Overridepublic void run() {for (int i = 0; i < 1000; i++) {set.add(i);}System.out.println("Thread-0线程执行完毕!");}
}public class Demo01CopyOnWriteArraySet {public static void main(String[] args) throws InterruptedException {//创建新线程对象并开启MyThread mt = new MyThread();mt.start();System.out.println("main线程继续执行");for (int i = 1000; i < 2000; i++) {MyThread.set.add(i);}System.out.println("main线程休息1秒");Thread.sleep(1000);System.out.println("最终集合的长度为:"+ MyThread.set.size());}
}

HashSet集合存在并发问题:

main线程会继续执行,往集合中添加元素
Thread-0线程开始执行了...往集合中添加元素!
Thread-0线程结束执行了!
main线程添加数据结束,等待1秒钟,等待Thread-0线程执行完毕
HashSet集合的总长度为:1996main线程会继续执行,往集合中添加元素
Thread-0线程开始执行了...往集合中添加元素!
Thread-0线程结束执行了!
main线程添加数据结束,等待1秒钟,等待Thread-0线程执行完毕
HashSet集合的总长度为:1999

3.并发Map集合ConcurrentHashMap(重点)

/*java.util.concurrent.ConcurrentHashMap<K,V>:多线程安全的双列集合,底层采用的是乐观锁(CAS机制),效率高java.util.Hashtable<K,V>:多线程安全的双列集合,底层采用的是悲观锁(synchronized,同步机制),效率低*/
public class MyThread extends Thread{//创建公共的静态的map集合//public static HashMap<Integer,Integer> map = new HashMap<>();//多线程不安全(数据不准确:2000->1986)//public static ConcurrentHashMap<Integer,Integer> map = new ConcurrentHashMap<>();//多线程安全public static Hashtable<Integer,Integer> map = new Hashtable<>();//多线程安全@Overridepublic void run() {for (int i = 0; i < 1000; i++) {map.put(i,i);}System.out.println("Thread-0线程执行结束!");}
}public class Demo01ConcurrentHashMap {public static void main(String[] args) throws InterruptedException {//创建新线程对象并开启MyThread mt = new MyThread();mt.start();System.out.println("main线程继续执行");for (int i = 1000; i < 2000; i++) {MyThread.map.put(i,i);}System.out.println("main线程休息1秒");Thread.sleep(1000);System.out.println("最终集合的长度为:"+ MyThread.map.size());}
}

HashMap集合存在并发问题:

main线程会继续执行,往集合中添加元素
Thread-0线程开始执行了...往集合中添加元素!
main线程添加数据结束,等待1秒钟,等待Thread-0线程执行完毕
Thread-0线程结束执行了!
HashMap集合的总长度为:1992

比较ConcurrentHashMap和Hashtable的效率

Java类库中,从1.0版本也提供一个线程安全的Map:Hashtable
Hashtable和ConcurrentHashMap有什么区别:
Hashtable采用的synchronized——悲观锁,效率更低。
ConcurrentHashMap:采用的CAS 机制——乐观锁,效率更高。

Hashtable容器使用synchronized来保证线程安全,但在线程竞争激烈的情况下Hashtable的效率非常低下。因为当一个线程访问Hashtable的同步方法,其他线程也访问Hashtable的同步方法时,会进入阻塞状态。如线程1使用put进行元素添加,线程2不但不能使用put方法添加元素,也不能使用get方法来获取元素,所以竞争越激烈效率越低。

4.多线程协作CountDownLatch

CountDownLatch允许一个或多个线程等待其他线程完成操作。

例如:线程1要执行打印:A和C,线程2要执行打印:B,但线程1在打印A后,要线程2打印B之后才能打印C,所以:线程1在打印A后,必须等待线程2打印完B之后才能继续执行。

CountDownLatch构造方法:

public CountDownLatch(int count)// 初始化一个指定计数器的CountDownLatch对象

CountDownLatch重要方法:

public void await() throws InterruptedException// 让当前线程等待
public void countDown() // 计数器进行减1

示例

//线程1  打印A、C
public class MyThreadAC extends Thread {private CountDownLatch countDownLatch;public MyThreadAC(CountDownLatch countDownLatch) {this.countDownLatch = countDownLatch;}@Overridepublic void run() {System.out.println("A");try {countDownLatch.await();//等待计数器归0执行} catch (InterruptedException e) {e.printStackTrace();}System.out.println("C");}
}//线程1 打印B
public class MyThreadB extends Thread {private CountDownLatch countDownLatch;public MyThreadB(CountDownLatch countDownLatch) {this.countDownLatch = countDownLatch;}@Overridepublic void run() {System.out.println("B");countDownLatch.countDown();//让计数器的值-1}
}public class Demo01Thread {public static void main(String[] args) throws InterruptedException {CountDownLatch cdl = new CountDownLatch(1);//创建1个计数器new MyThreadAC(cdl).start();Thread.sleep(1000);new MyThreadB(cdl).start();}
}

执行结果:
会保证按:A B C的顺序打印。

说明:
CountDownLatch中count down是倒数的意思,latch则是门闩的含义。整体含义可以理解为倒数的门栓,似乎有一点“三二一,芝麻开门”的感觉。

CountDownLatch是通过一个计数器来实现的,每当一个线程完成了自己的任务后,可以调用countDown()方法让计数器-1,当计数器到达0时,调用CountDownLatch。

await()方法的线程阻塞状态解除,继续执行。

多线程协作CyclicBarrier

概述

CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。

例如:公司召集5名员工开会,等5名员工都到了,会议开始。

我们创建5个员工线程,1个开会线程,几乎同时启动,使用CyclicBarrier保证5名员工线程全部执行后,再执行开会线程。

CyclicBarrier构造方法:

public CyclicBarrier(int parties, Runnable barrierAction)// 用于在线程到达屏障时,优先执行barrierAction,方便处理更复杂的业务场景

CyclicBarrier重要方法:

public int await()// 每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞

示例

//会议线程
public class PersonThread extends Thread {private CyclicBarrier cb;public PersonThread(CyclicBarrier cb) {this.cb = cb;}@Overridepublic void run() {try {Thread.sleep((int) (Math.random() * 1000));System.out.println(Thread.currentThread().getName()+"...来到了会场!");cb.await();//cb内部会将计数器 - 1} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}}
}//会议线程
public class MeetingThread extends Thread{@Overridepublic void run() {System.out.println("人齐了,开始开会了!");}
}//测试类
public class Demo {public static void main(String[] args) {//等待5个线程执行完毕,再执行MeetingThreadCyclicBarrier cb = new CyclicBarrier(5,new MeetingThread());PersonThread  p1 = new PersonThread(cb);PersonThread  p2 = new PersonThread(cb);PersonThread  p3 = new PersonThread(cb);PersonThread  p4 = new PersonThread(cb);PersonThread  p5 = new PersonThread(cb);p1.start();p2.start();p3.start();p4.start();p5.start();}
}

执行结果:

 Thread-3...来到了会场!
Thread-2...来到了会场!
Thread-5...来到了会场!
Thread-1...来到了会场!
Thread-4...来到了会场!
人齐了,开始开会了!

使用场景

使用场景:CyclicBarrier可以用于多线程计算数据,最后合并计算结果的场景。
需求:使用两个线程读取2个文件中的数据,当两个文件中的数据都读取完毕以后,进行数据的汇总操作。

6.并发数量控制SemaphoreSemaphore的主要作用是控制线程的并发数量。

synchronized可以起到"锁"的作用,但某个时间段内,只能有一个线程允许执行。

Semaphore可以设置同时允许几个线程执行。

Semaphore字面意思是信号量的意思,它的作用是控制访问特定资源的线程数目。

Semaphore构造方法:

public Semaphore(int permits)                        permits 表示许可线程的数量
public Semaphore(int permits, boolean fair)         fair 表示公平性,如果这个设为 true 的话,下次执行的线程会是等待最久的线程

Semaphore重要方法

public void acquire() 表示获取许可  lock
public void release() 表示释放许可  unlock

示例:同时允许两个线程执行

//教室线程
public class ClassRoom {private Semaphore semaphore = new Semaphore(2);public void info() throws InterruptedException {semaphore.acquire();//开始同步System.out.println(Thread.currentThread().getName()+"..来到了教室!");Thread.sleep(2000);//参观2秒System.out.println(Thread.currentThread().getName()+"..离开了教室!");semaphore.release();//结束同步}
}//学生线程
public class StudentThread extends Thread{private ClassRoom classRoom;public StudentThread(ClassRoom classRoom) {this.classRoom = classRoom;}@Overridepublic void run() {try {classRoom.info();//学生进入教室} catch (InterruptedException e) {e.printStackTrace();}}
}//测试类
public class Demo {public static void main(String[] args) {//创建一个教室ClassRoom classRoom = new ClassRoom();//循环创建5名学员for (int i = 0; i < 5; i++) {new StudentThread(classRoom).start();}}
}

执行结果:

Thread-1..来到了教室!
Thread-0..来到了教室!
Thread-1..离开了教室!
Thread-0..离开了教室!
Thread-2..来到了教室!
Thread-3..来到了教室!
Thread-3..离开了教室!
Thread-2..离开了教室!
Thread-4..来到了教室!
Thread-4..离开了教室!

7.线程信息交互Exchanger

概述

Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交换。

这两个线程通过exchange方法交换数据,如果第一个线程先执行exchange()方法,它会一直等待第二个线程也执行exchange方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。

Exchanger构造方法:

public Exchanger()

Exchanger重要方法:

public V exchange(V x) 参数传递给对方的数据,返回值接收对方返回的数据

示例一:exchange方法的阻塞特性

1).制作线程A,并能够接收一个Exchanger对象:

public class ThreadA extends Thread {private Exchanger<String> exchanger;public ThreadA(Exchanger<String> exchanger) {this.exchanger = exchanger;}@Overridepublic void run() {System.out.println("线程A开始执行");System.out.println("线程A给线程B100元,并从线程B得到一个电影票");String result = null;try {result = exchanger.exchange("100元");System.out.println("线程A得到的东西:"+result);} catch (InterruptedException e) {e.printStackTrace();}}
}

2).制作线程B

public class ThreadB extends Thread {private Exchanger<String> exchanger;public ThreadB(Exchanger<String> exchanger) {this.exchanger = exchanger;}@Overridepublic void run() {System.out.println("线程B开始执行");System.out.println("线程B给线程A一张电影票,并从线程A得到100元钱");String result = null;try {result = exchanger.exchange("电影票");System.out.println("线程B得到的东西:"+result);} catch (InterruptedException e) {e.printStackTrace();}}
}

测试类

public class Demo {public static void main(String[] args) {//创建Exchanger对象Exchanger<String> exchanger = new Exchanger<>();new ThreadA(exchanger).start();new ThreadB(exchanger).start();}
}

执行结果:

线程A开始执行
线程A给线程B100元,并从线程B得到一个电影票
线程B开始执行
线程B给线程A一张电影票,并从线程A得到100元钱
线程A得到的东西:电影票
线程B得到的东西:100元

示例2:exchanger方法的超时

//A线程
public class ThreadA extends Thread {private Exchanger<String> exchanger;public ThreadA(Exchanger<String> exchanger) {this.exchanger = exchanger;}@Overridepublic void run() {System.out.println("线程A开始执行");System.out.println("线程A给线程B100元,并从线程B得到一个电影票");String result = null;try {result = exchanger.exchange("100元",5, TimeUnit.SECONDS);System.out.println("线程A得到的东西:"+result);} catch (InterruptedException e) {e.printStackTrace();} catch (TimeoutException e) {System.out.println("5秒钟没等到线程B的值,线程A结束!");}}
}//测试类
public class Demo {public static void main(String[] args) {//创建Exchanger对象Exchanger<String> exchanger = new Exchanger<>();new ThreadA(exchanger).start();}
}

结果:

线程A开始执行
线程A给线程B100元,并从线程B得到一个电影票
5秒钟没等到线程B的值,线程A结束!

使用场景

使用场景:可以做数据校对工作

需求:比如我们需要将纸制银行流水通过人工的方式录入成电子银行流水。为了避免错误,采用AB岗两人进行录入,录入到两个文件中,系统需要加载这两个文件,

并对两个文件数据进行校对,看看是否录入一致,

解决多线程高并发问题常用类相关推荐

  1. SpringBoot实战实现分布式锁一之重现多线程高并发场景

    实战前言:上篇博文我总体介绍了我这套视频课程:"SpringBoot实战实现分布式锁" 总体涉及的内容,从本篇文章开始,我将开始介绍其中涉及到的相关知识要点,感兴趣的小伙伴可以关注 ...

  2. 多线程高并发 底层锁机制与优化的最佳实践——各种锁的分类 || synchronized 关键字 倒底锁的是什么东西?|| CAS与ABA问题||锁优化||轻量级锁一定比重量级锁的性能高吗

    多线程高并发 底层锁机制与优化的最佳实践 各种锁的分类 加上synchronized 关键字,共享资源就不会出错 synchronized 关键字 倒底锁的是什么东西? synchronized 锁的 ...

  3. 多线程高并发编程MySQL数据库处理

    本文作者:陈进坚 个人博客:https://jian1098.github.io CSDN博客:https://blog.csdn.net/c_jian 简书:https://www.jianshu. ...

  4. python访问数据库如何解决高并发_怎样解决数据库高并发的问题

    怎样解决数据库高并发的问题?解决数据库高并发使用缓存式的Web应用程序架构.增加Redis缓存数据库.增加数据库索引.页面静态化.使用存储过程.MySQL主从读写分离.分表分库.负载均衡集群. 解决数 ...

  5. 【多线程高并发】深入理解JMM产生的三大问题【原子性、可见性、有序性】

    [多线程&高并发]深入浅出原子性 https://blog.csdn.net/Kevinnsm/article/details/121681785?spm=1001.2014.3001.550 ...

  6. 【多线程高并发】深入浅出可见性

    [多线程&高并发]深入浅出原子性 https://blog.csdn.net/Kevinnsm/article/details/121681785?spm=1001.2014.3001.550 ...

  7. 解决数据库高并发的常见方案

    解决数据库高并发的常见方案: 1) 缓存式的 Web 应用程序架构: 在 Web 层和 DB(数据库)层之间加一层 cache 层,主要目的:减少数据库读取负担,提高数 据读取速度.cache 存取的 ...

  8. 解决redis高并发问题的几种思路

    解决redis高并发问题的几种思路 1:布隆过滤器 首先,布隆过滤器能解决绝大部分恶意攻击的请求,比如我们数据库中的id通常都设为自增的,是有一定范围大小的,如果有黑客恶意用数据库中没有的id一直访问 ...

  9. oracle 锁表如何解决_Java高并发解决什么方式

    对于我们开发的网站,如果网站的访问量非常大的话,那么我们就需要考虑相关的并发访问问题了.而并发问题是绝大部分的程序员头疼的问题,但话又说回来了,既然逃避不掉,那我们就坦然面对吧~今天就让我们一起来研究 ...

最新文章

  1. 823专业课计算机,辽宁科技大学823计算机专业基础综合(含数据结构、计算机组成原理、操作系统和计算机网络)考研复习经验...
  2. hdu 4747 mex 线段树+思维
  3. wifi rssi 计算 距离_WiFi和WLAN是一样的?真相在这里~别再傻傻分不清了
  4. 子网掩码、最大主机、最大子网数的计算
  5. ASP.NET Request.UrlReferrer 问题
  6. python中用于绘制各种图形的区域称作_Python--matplotlib绘图可视化知识点整理(示例代码)...
  7. pythondocx更新目录_python根目录
  8. 电脑应该如何升级bios版本
  9. ddos php源码,ddos PHP版_php
  10. Oracle Instant Client
  11. 经典场效应管如何快速关断技巧-KIA MOS管
  12. mac怎么无线打印机连接到服务器,Mac上wifi打印机怎么用 wifi打印机连接使用教程...
  13. Linux Stress测压工具安装与使用详解
  14. ExcelVBA:请注意!文档部分内容可能包含文档检查器无法删除的个人信息。
  15. protocol buffer 使用
  16. Exception in thread “main“ java.nio.file.NoSuchFileException: 123.txt
  17. 6个适合成年人的自学网站,每天半小时改变你一生
  18. 求解矩阵特征值的QR算法
  19. awl伪装MAC地址进行多线程SYN攻击
  20. 用文本编辑器编译cs文件

热门文章

  1. python 3.x 163邮箱登陆,邮件读取
  2. 乐观锁和悲观锁的区别
  3. 一体化电机在物流自动分拣系统中的应用
  4. Centos7.2下面解压.tar.gz 和.gz文件解压的方式
  5. ubuntu系统配置nijia_使用Apache/Nginx在Ubuntu 18.04上安装InvoiceNinja的方法
  6. 太原学院计算机15级花名册,我们是小小模范生!点击收获这份青春的花名册
  7. 好用的免费录屏工具captura
  8. 单片机程序读取烧录方法记录
  9. Java集合04 - RandomAccess
  10. 12.ROS编程学习:ROS常用指令