(四)Java多线程

编程入门 行业动态 更新时间:2024-10-06 04:08:06

(四)Java<a href=https://www.elefans.com/category/jswz/34/1767532.html style=多线程"/>

(四)Java多线程

1、CountDownLatch 倒数门栓

代码解释:
门栓计数100,每一个线程结束的时候我让latch.countDown(),然后所有线程start(),再latch.await()
最后结束。作用:
latch.await() 的作用就相当于门卫,每个线程执行完成都会减一,直到=0时,门卫就会开门。
然后执行>后面的代码。相比join更为灵活
/*** 多线程生成雪花ID*/public static void main(String[] args) throws InterruptedException {//计时开始时间long start = System.currentTimeMillis();//让100个线程同时进行final CountDownLatch latch = new CountDownLatch(100);//判断生成的20万条记录是否有重复记录final Map<Long, Integer> map = new ConcurrentHashMap();for (int i = 0; i < 100; i++) {//创建100个线程new Thread(() -> {for (int s = 0; s < 2000; s++) {long snowID = SnowIdUtils.uniqueLong();log.info("生成雪花ID={}",snowID);Integer put = map.put(snowID, 1);if (put != null) {throw new RuntimeException("主键重复");}}latch.countDown();}).start();}//让上面100个线程执行结束后,在走下面输出信息latch.await();log.info("生成20万条雪花ID总用时={}", System.currentTimeMillis() - start);}
join 用法示例
代码解释:
启动10个线程去累加一万次。正确结果为10万。count 是使用CAS原子类的计数方法
c是传统的synchronized锁的计数方法
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;/*** @program: bike-lease* @description: test* @author: Jack.Fang* @date:2021-06-24 下午 3:20**/
public class Test {AtomicInteger count = new AtomicInteger(0);int c = 0;void m(){for(int i=0;i<10000;i++){count.incrementAndGet();synchronized (this){c++;}}}public static void main(String[] args) {Test test = new Test();List<Thread> threadList = new ArrayList<>();for(int i=0;i<10;i++){threadList.add(new Thread(test::m,"thread-"+i));}threadList.forEach((o) -> o.start());threadList.forEach((o) -> {try{o.join();}catch (Exception e){e.printStackTrace();}});System.out.println(test.count);System.out.println(test.c);}
}

2、CyclicBarrier 循环栅栏

什么时候人满了就把栅栏推到,让、把人放出去,然后栅栏又立起来继续循环。。。代码解释:
两个参数,第二个参数可以不传也可以,就是满了什么也不做。
第一个参数是20,满了之后就帮我调用第二个参数指定的动作,就是一个Runnable对象。等够20人
则执行barrier.await()
CyclicBarrier barrier = new CyclicBarrier(20,()-> System.out.println("满人"));
for(int i=0;i<100;i++){new Thread(()->{try {barrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}}).start();
}

3、Phaser 阶段

它更像是结合了 CountDownLatch CyclicBarrier 。Phaser 是按照不同的阶段来对线程进行执行。代码解释:
模拟结婚的场景,有好多人参加,因此写了一个Person类,是一个Runnable可以new处理,扔给Thread去执行,
模拟我们每个人要做一些操作,有这些方法:arrive()到达,eat()吃,leave()离开,hug()拥抱。作为一个婚礼来说它会分为好几个阶段,第一阶段大家都得到齐了,第二阶段大家开始吃饭,三阶段大家离开,
第四个阶段新郎新娘入洞房。每个人都有这几个方法,在方法的实现里头就简单的睡1000毫秒。再看主程序,一共有5个人参加婚礼了,接下来新郎新娘参加婚礼,一共7个人。它一start就好调用我们得run()
方法,它会挨着牌的调用每一个阶段的方法。那好,我们在每一个阶段是不是得控制人数,第一个阶段得要人到期
了才能开始,二阶段所有人吃饭,三阶段所有人都离开,但是,到了四阶段进入洞房的时候就只能是新郎新娘参与了
所以,要模拟一个程序就要把整个过程很好几个阶段,而且每个阶段必须要等这些线程给我刚完事了你才能进入
下一个阶段。怎么模拟阶段,首先定义了一个phaser,这个phaser是从Phaser这个类继承,重写onAdvance方法,前进,
新城抵达这个栅栏的时候,所有的线程都满足了这个第一个栅栏的条件了onAdvance会被自动调用,
目前我们有好几个阶段,这个阶段是被写死的,必须是数字0开始,onAdvance会传来两个参数phaser
是第几个阶段registeredParties是目前这个阶段有几个人参加,每一个阶段都有一个打印,返回false,
一直到最后一个阶段返回true,所有线程结束,整个栅栏组,Phaser栅栏组就结束了。怎么才能让我的线程再一个栅栏面前给停住呢,就是调用phaser.arriveAndAwaitAdvance()这个方法,
这个方法的意思是到达等待继续往前走,知道新郎新娘入洞房,其他人不再参与,
调用phaser.arriveAndDeregister()这个方法。还有可以调用方法phaser.register()往上加,
不仅可以控制栅栏上的个数还可以控制栅栏上的等待数量,这个就叫做phaser。这个主要拓宽知识面用的

import java.util.Random;
import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;/*** @program: bike-lease* @description: 阶段线程* @author: Jack.Fang* @date:2021-06-28 下午 2:30**/
public class TestPhaser {static Random r = new Random();static MarriagePhaser phaser = new MarriagePhaser();static void milliSleep(int milli){try {TimeUnit.MICROSECONDS.sleep(milli);}catch (InterruptedException e) {e.printStackTrace();}}public static void main(String[] args) {phaser.bulkRegister(7);for(int i=0;i<5;i++){new Thread(new Person("p"+i)).start();}new Thread(new Person("新郎")).start();new Thread(new Person("新娘")).start();}static class MarriagePhaser extends Phaser{@Overrideprotected boolean onAdvance(int phase, int registeredParties) {switch (phase){case 0:System.out.println("所有人到齐了!"+registeredParties);System.out.println();return false;case 1:System.out.println("所有人都吃完了!"+registeredParties);System.out.println();return false;case 2:System.out.println("所有人都离开了!"+registeredParties);System.out.println();return false;case 3:System.out.println("婚礼结束!新郎新娘抱抱!"+registeredParties);return true;default:return true;}}}static class Person implements Runnable{String name;public Person(String name){this.name=name;}public void arrive(){milliSleep(r.nextInt(1000));System.out.printf("%s 到达现场!\n",name);phaser.arriveAndAwaitAdvance();}public void eat(){milliSleep(r.nextInt(1000));System.out.printf("%s 吃完!\n",name);phaser.arriveAndAwaitAdvance();}public void leave(){milliSleep(r.nextInt(1000));System.out.printf("%s 离开!\n",name);phaser.arriveAndAwaitAdvance();}public void hug(){if(name.equals("新郎")||name.equals("新娘")){milliSleep(r.nextInt(1000));System.out.printf("%s 洞房!\n",name);phaser.arriveAndAwaitAdvance();}else{phaser.arriveAndDeregister();}}@Overridepublic void run() {arrive();eat();leave();hug();}}}

4、ReadWriteLock 读写锁

概念其实就是共享锁和排他锁,读锁就是共享锁,写锁就排他锁。代码解释:
例如获取公司架构,读多写少。要求他不产生数据不一致的情况。可以做成这种锁,当读线程上来的时候加一把锁
是允许其他读线程可以读,写线程来了我不给它,你先别写,等我读完了你在写。读线程进来的时候我们大家
一起读,因为你不改原来的内容,写线程上来把整个线程全锁定,你先不要读,等我写完你再读。读写锁怎么用,我们这有两个方法,read()读一个数据,write()写一个数据。read这个数据的是否我们需要
你往里面传一把锁,这个传那把锁你自己定,我们可以划船自己定义的全部都是排他锁,也可以传读写锁里面的
读锁或写锁。write的时候也需要传把锁,同事需要你传一个新值完成后解锁unlock。我们现在的问题是往里传这个lock有两种方法,第一种直接new ReentrantLock()传进去,分析下这种方法,
主程序定义了一个Runnable对象,第一个是调用read()方法,第二个是调用write()方法同时往里头扔一个
随机值,然后起了18个读线程,起了2个写线程,这个两个我要想执行完的话,我现在传的是一个ReentranLock,
这把锁上了之后没有其他任何人可以拿到这把锁,而这里面每一个线程执行都需要1秒钟,在这种情况下你必须
得等20秒才能干完这事儿;第二种,我们换了锁 new ReentrantReadWriteLock()是ReadWriteLock的一种实现,
在这种思想里头我又分出两把锁来,读锁我就拿到了。
通过readWriteLock.writeLock()拿到writeLock对象。这两把锁在我读的时候登进去,因此,读线程是可以一起读的,也就是说这18个线程可以一秒钟完成工作结束,
所以使用读写锁效率会大大的提升。
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;/*** @program: bike-lease* @description: 读写锁* @author: Jack.Fang* @date:2021-06-28 下午 3:02**/
public class ReadWriterLock {static Lock lock = new ReentrantLock();private static int value;static ReentrantReadWriteLock readWriterLock = new ReentrantReadWriteLock();static Lock readLock = readWriterLock.readLock();static Lock writeLock = readWriterLock.writeLock();public static void read(Lock lock){try{lock.lock();Thread.sleep(1000);System.out.println("read over!");} catch (InterruptedException e) {e.printStackTrace();}finally {lock.unlock();}}public static void write(Lock lock,int v){try{lock.lock();Thread.sleep(1000);value = v;System.out.println("write over!");} catch (InterruptedException e) {e.printStackTrace();}finally {lock.unlock();}}public static void main(String[] args) {
//        Runnable readR = ()->read(lock);Runnable readR = ()->read(readLock);//        Runnable writeR = ()->write(lock,new Random().nextInt());Runnable writeR = ()->write(writeLock,new Random().nextInt());for(int i=0;i<18;i++) new Thread(readR).start();for(int i=0;i<2;i++) new Thread(writeR).start();}}

5、Semaphore 信号灯

可以往里面传一个数,permits是允许的数量,你可以想着有几盏信号灯,一个灯你们闪着数字表示到底
允许几个来参考我这个信号灯。s.acquire()这个方法叫阻塞方法,阻塞方法的意思是说我大概acquire不到的话我就停在这,
acquire的意思就是得到。如果我Semaphore s = new Semaphore(1)写的是1,我取一下,
acquire一下他就变成0,当变成0之后别人是acquire不到的,然后继续执行,线程结束之后注意
要s.release(),执行完该方法就会把0变成1,还原化。Semaphore的含义就是限流,比如说你在买票,Semaphore写5就是只能有5个人可以同时买票。
acquire的意思叫获得这把锁,线程如果想要继续往下执行,必须得从Semaphore里面获得一个许可,
他一共有5个许可用到0你就得给我等着。例如有一个8条车道的机动车,这里只有2个收费站,到这儿,谁acquire得到其中一个谁执行,
默认Semaphore是非公平的,new Semaphore(2,true)第二个值传true才是设置公平。
公平这个事儿是有一堆队列在哪儿等,大家伙过来排队,用这个车道和收费站来举例,
就是我们有4辆车都在等着进入一个车道,然后买再来一辆新的时候,他不会超到前面去,
要在后面排队这叫公平。所以说内部是有队列的。从上面的CountDownLatch 、CyclicBarrier 、Phaser、
ReadWriteLock、Semaphore、Exchanger都是用用一个队列,同一个类来实现的,这个类叫AQS
import java.util.concurrent.Semaphore;/*** @program: bike-lease* @description: 信号灯* @author: Jack.Fang* @date:2021-06-28 下午 3:48**/
public class TestSemaphore {public static void main(String[] args) {//非公平
//      Semaphore s = new Semaphore(2);//公平Semaphore s = new Semaphore(2,true);//允许一个线程同时执行
//      Semaphore s = new Semaphore(1);new Thread(()->{try{s.acquire();System.out.println("T1 running...");Thread.sleep(1000);System.out.println("T1 running...");} catch (InterruptedException e) {e.printStackTrace();}finally {s.release();}}).start();new Thread(()->{try{s.acquire();System.out.println("T2 running...");Thread.sleep(1000);System.out.println("T2 running...");s.release();} catch (InterruptedException e) {e.printStackTrace();}}).start();}
}

6、Exchanger 交换机

Exchanger 主要是拓展知识面用的,两人之间互相交换数据用的。代码解释:
第一个相册有一个成员变量s,然后exchanger.exchange(s),第二个也是这样,t1线程名字叫T1,
第二个线程名字叫T2。到最后,打印出来你会发现他们两交换了一下。线程间通信的方式非常多,
这只是其中一种,就是线程之间交换数据用的。exchanger可以想象是一个容器,容器中有2个值,两个线程,有两个格子的未知,第一个相册执行到
exchanger.exchange的时候,阻塞,但是要注意我这个exchange方法的是否是往里面扔了一个值,
你可以认为把T1扔到了第一个格子,然后第二个线程开始执行,也执行到这句话了,exchange,
他把自己的这个值T2扔到第二个格子里,接下来这两个值交换了一个下,T1扔给T2,T2扔给T1。
两个线程继续往前跑。exchange只能是两个线程之间,交换这个东西只能两两进行。exchange的使用场景,比如在游戏中两个人装备交换。
import java.util.concurrent.Exchanger;/*** @program: bike-lease* @description: 交换机* @author: Jack.Fang* @date:2021-06-28 下午 4:12**/
public class TestExchanger {static Exchanger<String> exchanger = new Exchanger<>();public static void main(String[] args) {new Thread(()->{String s = "T1";try{s = exchanger.exchange(s);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+" "+s);},"t1").start();new Thread(()->{String s = "T2";try{s = exchanger.exchange(s);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+" "+s);},"t2").start();}
}

更多推荐

(四)Java多线程

本文发布于:2024-02-27 22:15:45,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1766372.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:多线程   Java

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!