Java多线程经典案例分享
案例一
实现一个容器,提供两个方法,add(),count() 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束。
本案例我通过闭锁(也叫门栓锁)实现,实现如下:
package day_12_28.zuoye;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
* @author soberw
* @Classname AddAndCount
* @Description 实现一个容器,提供两个方法,add,count 写两个线程,
* 线程1添加10个元素到容器中,线程2实现监控元素的个数,
* 当个数到5个时,线程2给出提示并结束。
* @Date 2021-12-28 10:45
*/
public class AddAndCount {
CountDownLatch cdl = new CountDownLatch(1);
List<String> list = new ArrayList<>();
public static void main(String[] args) {
var aac = new AddAndCount();
new Thread(aac::add, "A").start();
new Thread(aac::count, "B").start();
}
void add() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
String item = String.format("%s - %d", "item", i);
list.add(item);
System.out.println(Thread.currentThread().getName() + ":" + item);
if (i == 4) {
cdl.countDown();
}
}
}
void count() {
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("程序结束...");
System.exit(0);
}
}
案例二
编写程序模拟死锁。
死锁,简单来说就是两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
下面我就模拟这一状态:
package day_12_28.zuoye;
/**
* @author soberw
* @Classname Deadlock
* @Description 编写程序模拟死锁
* @Date 2021-12-28 10:59
*/
public class Deadlock {
private final Object o1 = new Object();
private final Object o2 = new Object();
public static void main(String[] args) {
Deadlock d = new Deadlock();
new Thread(d::m1).start();
new Thread(d::m2).start();
}
void m1(){
System.out.println(Thread.currentThread().getName() + "启动等待...");
synchronized(o1){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(o2){
System.out.println("哈哈..");
}
}
}
void m2(){
System.out.println(Thread.currentThread().getName() + "启动等待...");
synchronized(o2){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(o1){
System.out.println("哈哈..");
}
}
}
}
案例三
编写程序,实现三个线程,运行输出 A1 B2 C3 A4 B5 C6 ……
我这里用了两种方式去实现:
方式一:
用公平锁:
package day_12_28.zuoye;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author soberw
* @Classname TurnNumber
* @Description 编写程序,实现三个线程,运行输出 A1 B2 C3 A4 B5 C6 ….. 用公平锁
* @Date 2021-12-28 14:09
*/
public class TurnNumber {
AtomicInteger num = new AtomicInteger(0);
private final ReentrantLock rl = new ReentrantLock(true);
public void show() {
for (; ; ) {
rl.lock();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
String tn = Thread.currentThread().getName();
int i = num.incrementAndGet();
String s = String.format("%s%d", tn, i);
System.out.print(s + " ");
if ("C".equals(tn)) {
System.out.println();
}
rl.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
TurnNumber tn = new TurnNumber();
Thread a = new Thread(tn::show, "A");
Thread b = new Thread(tn::show, "B");
Thread c = new Thread(tn::show, "C");
a.setPriority(Thread.MAX_PRIORITY);
a.start();
b.setPriority(Thread.NORM_PRIORITY);
b.start();
c.setPriority(Thread.MIN_PRIORITY);
c.start();
}
}
方式二:
用join() 方法
package day_12_28.zuoye;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author soberw
* @Classname TurnNumber
* @Description 编写程序,实现三个线程,运行输出 A1 B2 C3 A4 B5 C6 ….. 第二种写法,用join()
* @Date 2021-12-28 14:09
*/
public class TurnNumber2 {
AtomicInteger num = new AtomicInteger(0);
public void show() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
String tn = Thread.currentThread().getName();
int i = num.incrementAndGet();
String s = String.format("%s%d", tn, i);
System.out.print(s + " ");
if ("C".equals(tn)) {
System.out.println();
}
}
public static void main(String[] args) throws InterruptedException {
TurnNumber2 tn = new TurnNumber2();
while (true) {
Thread a = new Thread(tn::show, "A");
Thread b = new Thread(tn::show, "B");
Thread c = new Thread(tn::show, "C");
a.setPriority(Thread.MAX_PRIORITY);
a.start();
a.join();
b.setPriority(Thread.NORM_PRIORITY);
b.start();
b.join();
c.setPriority(Thread.MIN_PRIORITY);
c.start();
c.join();
}
}
}
案例四
创建五个线程并进入等待状态,等两秒后主线程开始并释放全部线程,最后主线程结束
本案例我用的是wait() 与notifyAll()组合形式;
package day_12_27;
import java.util.concurrent.TimeUnit;
/**
* @author soberw
* @Classname WaitAndNotify
* @Description 创建五个线程并进入等待状态,等两秒后主线程开始并释放全部线程,最后主线程结束
* @Date 2021-12-27 15:47
*/
public class WaitAndNotify {
public static void main(String[] args) {
Object co = new Object();
for (int i = 0; i < 5; i++) {
MyThread t = new MyThread("Thread" + i, co);
t.start();
}
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("-----Main Thread notify-----");
synchronized (co) {
//co.notify();
co.notifyAll();
}
TimeUnit.SECONDS.sleep(2);
System.out.println("Main Thread is end.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class MyThread extends Thread {
private String name;
private Object co;
public MyThread(String name, Object o) {
this.name = name;
this.co = o;
}
@Override
public void run() {
System.out.println(name + " is waiting.");
try {
synchronized (co) {
co.wait();
}
System.out.println(name + " has been notified.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
案例五
用五个线程实现,求123456789 之间放±和100的表达式,如果一个线程求出结果,立即告诉其它停止。
这里我用到了AtomicBoolean原子类来保证数据的原子性:
package day_12_27.zuoye;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author soberw
* @Classname Number100
* @Description 用五个线程实现,求123456789 之间放+-和100的表达式,如果一个线程求出结果,立即告诉其它停止。
* @Date 2021-12-27 21:14
*/
public class Number100 {
//原子类,保证原子性
AtomicBoolean ab = new AtomicBoolean(true);
public void show() {
String[] ss = {"", "+", "-"};
StringBuilder sbu = new StringBuilder();
sbu.append("1");
Random random = new Random();
while (ab.get()) {
for (int i = 2; i < 9; i++) {
sbu.append(ss[random.nextInt(3)]);
sbu.append(i);
}
Pattern p = Pattern.compile("[0-9]+|-[0-9]+");
Matcher m = p.matcher(sbu.toString());
int sum = 0;
while (m.find()) {
sum += Integer.parseInt(m.group());
}
if (sum == 100) {
ab.set(false);
System.out.println(Thread.currentThread().getName() + ":" + sbu.toString() + " = 100");
}
sbu.delete(1, sbu.length());
}
}
public static void main(String[] args) {
var n = new Number100();
for (int i = 0; i < 5; i++) {
new Thread(n::show).start();
}
}
}
案例六
模拟经典问题,生产者-消费者问题:
方式一:
package day_12_28.zuoye;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author soberw
* @Classname ProductorAndConsumerForLock
* @Description 用线程通信机制解决生产者消费者问题
* @Date 2021-12-28 19:18
*/
public class ProductorAndConsumerForLock {
public static void main(String[] args) {
Clerk1 clerk1 = new Clerk1();
Productor1 pro = new Productor1(clerk1);
Consumer1 con = new Consumer1(clerk1);
new Thread(pro, "生产者 A").start();
new Thread(con, "消费者 B").start();
// new Thread(pro, "生产者 C").start();
// new Thread(con, "消费者 D").start();
}
}
class Clerk1 {
private int product = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
// 进货
public void get() {
lock.lock();
try {
if (product >= 1) { // 为了避免虚假唤醒,应该总是使用在循环中。
System.out.println("产品已满!");
try {
condition.await();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : "
+ ++product);
condition.signalAll();
} finally {
lock.unlock();
}
}
// 卖货
public void sale() {
lock.lock();
try {
if (product <= 0) {
System.out.println("缺货!");
try {
condition.await();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : "
+ --product);
condition.signalAll();
} finally {
lock.unlock();
}
}
}
// 生产者
class Productor1 implements Runnable {
private Clerk1 clerk1;
public Productor1(Clerk1 clerk1) {
this.clerk1 = clerk1;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk1.get();
}
}
}
// 消费者
class Consumer1 implements Runnable {
private Clerk1 clerk1;
public Consumer1(Clerk1 clerk1) {
this.clerk1 = clerk1;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk1.sale();
}
}
}
方式二:
package day_12_28.zuoye;
/**
* @author soberw
* @Classname ProducerAndConsumer
* @Description 用等待唤醒机制解决生产者消费者问题
* @Date 2021-12-28 16:25
*/
public class ProductorAndConsumer {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor pro = new Productor(clerk);
Consumer cus = new Consumer(clerk);
new Thread(pro, "生产者 A").start();
new Thread(cus, "消费者 B").start();
new Thread(pro, "生产者 C").start();
new Thread(cus, "消费者 D").start();
}
}
//店员
class Clerk {
private int product = 0;
//进货
public synchronized void get() {//循环次数:0
//为了避免虚假唤醒问题,应该总是使用在循环中
while (product >= 1) {
System.out.println("产品已满!");
try {
this.wait();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : " + ++product);
this.notifyAll();
}
//卖货
public synchronized void sale() {//product = 0; 循环次数:0
while (product <= 0) {
System.out.println("缺货!");
try {
this.wait();
} catch (InterruptedException 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();
}
}
}
案例七
开十个线程打印输出1~10000中偶数的值,计算总耗时
我采用的是闭锁机制:
package day_12_28.zuoye;
import java.util.concurrent.CountDownLatch;
/**
* @author soberw
* @Classname CountTime
* @Description 开十个线程打印输出1~10000中偶数的值,计算总耗时 用闭锁(门栓)
* @Date 2021-12-28 15:22
*/
public class CountTime {
static CountDownLatch cdl = new CountDownLatch(10);
void show() {
for (int i = 0; i < 10000; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
cdl.countDown();
}
public static void main(String[] args) {
CountTime ct = new CountTime();
long start = System.currentTimeMillis();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
new Thread(ct::show).start();
}
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println((end - start - 100) + "--------------");
}
}
Java多线程经典案例分享的更多相关文章
- JAVA多线程经典问题 -- 生产者 消费者
工作2年多来一直也没有计划写自己的技术博客,最近辞职在家翻看<thingking in JAVA>,偶尔看到了生产者与消费者的一个经典的多线程同步问题.本人在工作中很少使用到多线程以及高并 ...
- Java基础经典案例
案例列表 01减肥计划switch版本 02减肥计划if版本 03逢七跳过 04不死神兔 05百钱白鸡 06数组元素求和 07判断两个数组是否相同 08查找元素在数组中的索引 09数组元素反转 10评 ...
- Java多线程分析案例
1. 多线程的创建方式 (1).继承 Thread类:但Thread本质上也是实现了Runnable 接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过 Thread 类的 sta ...
- JAVA多线程经典问题 -- 生产者 消费者 同步队列实现方法
在JAVASE5 中的java.util.concurrent.BlockingQueue支持,BlockingQueue是一个接口但是我们通常可以使用LinkedBlockingQueue,它是一个 ...
- 《开源公开课分享》:Java开源框架案例分享
缺乏高端技术人才?缺乏开发标准? 代码复用性低?技术风险难于把控? 招聘成本高?培训成本高? 假设想法不够雄伟,那么就会局限于细节:假设一開始就铺很大的摊子,将会失去控制: ...
- Java多线程经典题目(医院挂号)
题目 实现一个医院的挂号机系统,要求:有多台挂号机同时运行,此时无论有多少患者挂号,要求都能挂到不同 的号码,并且要求实现当意外断电之后,下一次恢复还能从上次结束号码继续挂号? * synchroni ...
- JAVA多线程之生产者 消费者模式 妈妈做面包案例
创建四个类 1.面包类 锅里只可以放10个面包 ---装面包的容器2.厨房 kitchen 生产面包 和消费面包 最多生产100个面包3.生产者4消费者5.测试类 多线程经典案例 import ja ...
- Java多线程中的wait/notify通信模式
前言 最近在看一些JUC下的源码,更加意识到想要学好Java多线程,基础是关键,比如想要学好ReentranLock源码,就得掌握好AQS源码,而AQS源码中又有很多Java多线程经典的一些应用:再比 ...
- java基础知识回顾之java Thread类学习(八)--java多线程通信等待唤醒机制经典应用(生产者消费者)
*java多线程--等待唤醒机制:经典的体现"生产者和消费者模型 *对于此模型,应该明确以下几点: *1.生产者仅仅在仓库未满的时候生产,仓库满了则停止生产. *2.消费者仅仅在有产品的时 ...
随机推荐
- Rainbond 对接 Istio 原理讲解和代码实现分析
一.背景 现有的 ServiceMesh 框架有很多,如 Istio.linkerd等.对于用户而言,在测试环境下,需要达到的效果是快.开箱即用.但在生产环境下,可能又有熔断.延时注入等需求.那么单一 ...
- STC8H开发(三): 基于FwLib_STC8的模数转换ADC介绍和演示用例说明
目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...
- 【爬虫】从零开始使用 Scrapy
一. 概述 最近有一个爬虫相关的需求,需要使用 scrapy 框架来爬取数据,所以学习了一下这个非常强大的爬虫框架,这里将自己的学习过程记录下来,希望对有同样需求的小伙伴提供一些帮助. 本文主要从下面 ...
- RocketMQ 介绍与安装
目录 RocketMQ 介绍 MQ 介绍 MQ 作用 MQ 缺点 MQ 常见产品 RocketMQ 简介 RocketMQ 架构 RocketMQ 安装 RocketMQ 介绍 MQ 介绍 定义: M ...
- JUC之线程池基础
线程池 定义和方法 线程池的工作时控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等待其他线程执行完成,再从队列中取出任 ...
- 我把自己的java库发布到了maven中央仓库,从此可以像Jackson、Spring的jar一样使用它了
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- virtual studio发布到gihub
问题 我们想要发布代码到github或者微软团队服务时候,往往发现没有本地库,所以难以发布. 解决方案 在解决方右击就可以新建git 文件都会出现小锁说明有了记录 文件夹会对应出现 右上角管理连接也会 ...
- elasticsearch算法之词项相似度算法(一)
一.词项相似度 elasticsearch支持拼写纠错,其建议词的获取就需要进行词项相似度的计算:今天我们来通过不同的距离算法来学习一下词项相似度算法: 二.数据准备 计算词项相似度,就需要首先将词项 ...
- IoC容器-Bean管理(bean生命周期)
1,生命周期 即从对象创建到对象销毁的过程 2,bean生命周期 (1)通过构造器创建bean实例(无参数构造) (2)为bean的属性设置值和对其他bean的引用(调用set方法) (3)调用bea ...
- js生成指定范围的随机整数
定义一个random()函数,原理是 随机数和最大值减最小值的差相乘 最后再加上最小值. function random(min, max) { return Math.floor(Math.rand ...