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.消费者仅仅在有产品的时 ...
随机推荐
- mysql语句1-创建库和表
一.DDL数据定义语言 就是对书库内部的对象进行创建.删除.修改等操作的语言. 关键字:create drop alter 1.连接数据库 mysql -u用户名 -p -h指定主机(不指定默认是 ...
- 利用js 引用的方式 鼠标经过弹出效果
js引用的是此网站代码: https://www.cnblogs.com/jq-growup/p/15609469.html <!DOCTYPE html> <html lang=& ...
- 初识python 之 自动拆分转换文本内容
上一篇升级版,转换文件内容. #!/user/bin env python # author:Simple-Sir # time:2021/7/9 23:32 def txt_2_list(filen ...
- 微信小程序配置域名的时候提示“校验文件验证失败”
在微信小程序后台配置web-view的业务域名跟扫普通链接二维码打开小程序两项功能时, 一直提示"校验文件验证失败,请下载校验文件,上传到服务器指定的目录" 实际访问校验文件的路径 ...
- Discuz!X V3.4后台任意文件删除
Discuz!X V3.4后台任意文件删除 简述 该漏洞为后台任意文件删除,需要有管理员的权限,所以说危害非常小 复现环境 docker.vulhub-master 项目地址:https://gite ...
- 网络编程-基于Websocket聊天室(IM)系统
目录 一.HTML5 - Websocket协议 二.聊天室(IM)系统的设计 2.1.使用者眼中的聊天系统 2.2.开发者眼中的聊天系统 2.3.IM系统的特性 2.4.心跳机制:解决网络的不确定性 ...
- jsp标签问题
在jsp页面使用标签过程中有时候不注意规则的话,eclipse会提示一些错误,下面针对这些错误提出相应的解决办法:<form></form>标签1. Invalid locat ...
- 春节将至,喜庆的烟花安排上(js实现烟花)
一年一度的春节即将来临,然后苦逼的我还在使劲的摸鱼,未能回家体验小时候路边放爆竹的快乐时光,所以只能在网上来实现这个小小的心愿了.烟花静态效果图如下: 为了大伙复制方便就不分开写,直接复制即可,具体实 ...
- Kubernetes 微服务最佳实践
本文由个人笔记 ryan4yin/knowledge 整理而来 本文主要介绍我个人在使用 Kubernetes 的过程中,总结出的一套「Kubernetes 配置」,是我个人的「最佳实践」. 其中大部 ...
- Vue+webpack配置实现多页面应用开发
为什么要配置多页面开发? · 由于单页面应用不利于SEO,对于某些资讯类网站不够友好,而多页面则能够更优的解决此问题. · 传统的多页面开发模式(如java的jsp等) 前后端耦合性大,开发效率低,代 ...