java基础-多线程应用案例展示
java基础-多线程应用案例展示
作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
一.两只熊,100只蜜蜂,蜜蜂每次生产的蜂蜜量是1,罐子的容量是30,熊在罐子的蜂蜜量达到20的时候,一次性将蜂蜜吃光。
/*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/%E5%B0%8F%E8%AF%95%E7%89%9B%E5%88%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.smallTestBullKnife; public class BeeDemo {
public static void main(String[] args) {
Box box = new Box();
Bear b1 = new Bear(box , "熊大");
Bear b2 = new Bear(box , "熊二");
b1.start();
b2.start();
for(int i = 1 ; i <= 100 ; i ++){
new Bee(box , "Bee" + i).start();
}
}
} /**
* 罐子
*/
class Box{
public static int MAX = 30 ;
private int size = 0 ; /**
* 添加蜂蜜方法
*/
public synchronized void add(int cap){
while((size + cap) > MAX){
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
size = size + cap ;
this.notifyAll();
} /**
* 清空罐子
*/
public synchronized int clear(){
while(size < 20){
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int temp = size ;
size = 0 ;
return temp ;
}
} /**
* 生产者
*/
class Bee extends Thread{
private String beeName ;
private Box box ;
private int production = 1;
public Bee(Box box ,String name){
this.beeName = name ;
this.box = box ;
} public void run() {
while(true){
box.add(production);
System.out.println(beeName + "生产了" + production +"蜂蜜");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} /**
* 消费者
*/
class Bear extends Thread{
private String bearName ;
private Box box ;
public Bear(Box box , String name){
this.bearName = name ;
this.box = box ;
} public void run() {
while(true){ int size = box.clear();
System.out.println(bearName + " 吃了【" + size +"】个蜂蜜");
}
}
}
二.有30个和尚,100个馒头,每个和尚最多吃4馒头,最少一个馒头,满足上述条件下,尽快把馒头吃没,使用多线程模拟,和尚就是线程。
/*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/%E5%B0%8F%E8%AF%95%E7%89%9B%E5%88%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.smallTestBullKnife; /**
* 和尚吃馒头问题
*/
public class MonkDemo {
public static void main(String[] args) {
Basket b = new Basket();
for(int i = 0 ; i < 30 ; i ++){
new Monk(b , "和尚" + (i+1)+":\t").start();
}
}
} /**
* 篮子,100个馒头
*/
class Basket{
//馒头数量==编号
private int count = 100 ;
//和尚数量 ==没吃馒头的和尚数量
private int numMonks = 30 ;
/**
* 获取馒头的方法,返回馒头的编号
*/
public synchronized int getBread(Monk monk){
//和尚第一次吃
if(monk.count == 0){
int temp = count ;
count -- ;
numMonks -- ;
return temp ;
}
//和尚还可以吃的情况
else if(monk.count < Monk.MAX){
//判断是否有多余的馒头
if(count > numMonks){
int temp = count ;
count -- ;
return temp ;
}
else{
return -1 ;
}
}
return -1 ;
}
} /**
* 和尚
*/
class Monk extends Thread{
private String monkName ;
private Basket basket ; //最少一个馒头
public static int MIN = 1 ;
//最多4个馒头
public static int MAX = 4 ; //吃的馒头数
public int count ; public Monk(Basket basket , String monkName){
this.basket = basket ;
this.monkName = monkName ;
} public void run() {
while(true){
int no = basket.getBread(this);
if(no ==-1){
break ;
}
else{
count ++ ;
System.out.println(monkName + "吃了编号为[" + no + "]的馒头");
}
}
System.out.println(monkName + "共吃了【" + count + "】馒头");
}
}
和尚2: 吃了编号为[100]的馒头
和尚3: 吃了编号为[99]的馒头
和尚3: 吃了编号为[96]的馒头
和尚3: 吃了编号为[95]的馒头
和尚3: 吃了编号为[94]的馒头
和尚3: 共吃了【4】馒头
和尚4: 吃了编号为[97]的馒头
和尚4: 吃了编号为[93]的馒头
和尚4: 吃了编号为[92]的馒头
和尚4: 吃了编号为[91]的馒头
和尚4: 共吃了【4】馒头
和尚2: 吃了编号为[98]的馒头
和尚2: 吃了编号为[90]的馒头
和尚2: 吃了编号为[89]的馒头
和尚2: 共吃了【4】馒头
和尚6: 吃了编号为[88]的馒头
和尚6: 吃了编号为[87]的馒头
和尚6: 吃了编号为[86]的馒头
和尚6: 吃了编号为[85]的馒头
和尚6: 共吃了【4】馒头
和尚1: 吃了编号为[84]的馒头
和尚1: 吃了编号为[82]的馒头
和尚8: 吃了编号为[83]的馒头
和尚8: 吃了编号为[80]的馒头
和尚8: 吃了编号为[79]的馒头
和尚8: 吃了编号为[78]的馒头
和尚8: 共吃了【4】馒头
和尚1: 吃了编号为[81]的馒头
和尚1: 吃了编号为[77]的馒头
和尚1: 共吃了【4】馒头
和尚5: 吃了编号为[76]的馒头
和尚5: 吃了编号为[75]的馒头
和尚12: 吃了编号为[74]的馒头
和尚12: 吃了编号为[71]的馒头
和尚12: 吃了编号为[70]的馒头
和尚12: 吃了编号为[69]的馒头
和尚12: 共吃了【4】馒头
和尚14: 吃了编号为[68]的馒头
和尚14: 吃了编号为[67]的馒头
和尚14: 吃了编号为[66]的馒头
和尚5: 吃了编号为[73]的馒头
和尚5: 吃了编号为[64]的馒头
和尚5: 共吃了【4】馒头
和尚14: 吃了编号为[65]的馒头
和尚14: 共吃了【4】馒头
和尚10: 吃了编号为[72]的馒头
和尚10: 吃了编号为[63]的馒头
和尚10: 吃了编号为[62]的馒头
和尚10: 吃了编号为[61]的馒头
和尚10: 共吃了【4】馒头
和尚9: 吃了编号为[60]的馒头
和尚9: 吃了编号为[59]的馒头
和尚7: 吃了编号为[58]的馒头
和尚7: 吃了编号为[56]的馒头
和尚7: 吃了编号为[55]的馒头
和尚11: 吃了编号为[54]的馒头
和尚11: 吃了编号为[52]的馒头
和尚11: 吃了编号为[51]的馒头
和尚9: 吃了编号为[57]的馒头
和尚9: 吃了编号为[48]的馒头
和尚9: 共吃了【4】馒头
和尚11: 吃了编号为[50]的馒头
和尚11: 共吃了【4】馒头
和尚7: 吃了编号为[53]的馒头
和尚15: 吃了编号为[45]的馒头
和尚15: 吃了编号为[44]的馒头
和尚15: 吃了编号为[42]的馒头
和尚15: 吃了编号为[41]的馒头
和尚15: 共吃了【4】馒头
和尚17: 吃了编号为[46]的馒头
和尚17: 吃了编号为[39]的馒头
和尚19: 吃了编号为[38]的馒头
和尚19: 吃了编号为[36]的馒头
和尚19: 吃了编号为[35]的馒头
和尚19: 吃了编号为[34]的馒头
和尚19: 共吃了【4】馒头
和尚13: 吃了编号为[47]的馒头
和尚13: 吃了编号为[33]的馒头
和尚13: 吃了编号为[32]的馒头
和尚13: 吃了编号为[31]的馒头
和尚13: 共吃了【4】馒头
和尚20: 吃了编号为[49]的馒头
和尚20: 吃了编号为[30]的馒头
和尚20: 吃了编号为[29]的馒头
和尚20: 吃了编号为[28]的馒头
和尚20: 共吃了【4】馒头
和尚22: 吃了编号为[26]的馒头
和尚22: 吃了编号为[25]的馒头
和尚22: 吃了编号为[23]的馒头
和尚22: 吃了编号为[22]的馒头
和尚22: 共吃了【4】馒头
和尚17: 吃了编号为[37]的馒头
和尚17: 吃了编号为[20]的馒头
和尚17: 共吃了【4】馒头
和尚18: 吃了编号为[40]的馒头
和尚18: 吃了编号为[19]的馒头
和尚18: 吃了编号为[18]的馒头
和尚18: 吃了编号为[17]的馒头
和尚18: 共吃了【4】馒头
和尚25: 吃了编号为[16]的馒头
和尚27: 吃了编号为[15]的馒头
和尚27: 吃了编号为[13]的馒头
和尚16: 吃了编号为[43]的馒头
和尚16: 吃了编号为[11]的馒头
和尚16: 吃了编号为[10]的馒头
和尚16: 吃了编号为[8]的馒头
和尚16: 共吃了【4】馒头
和尚26: 吃了编号为[7]的馒头
和尚7: 共吃了【4】馒头
和尚26: 吃了编号为[6]的馒头
和尚26: 吃了编号为[4]的馒头
和尚26: 吃了编号为[3]的馒头
和尚26: 共吃了【4】馒头
和尚29: 吃了编号为[5]的馒头
和尚30: 吃了编号为[2]的馒头
和尚28: 吃了编号为[9]的馒头
和尚28: 共吃了【1】馒头
和尚27: 吃了编号为[12]的馒头
和尚27: 共吃了【3】馒头
和尚25: 吃了编号为[14]的馒头
和尚25: 共吃了【2】馒头
和尚24: 吃了编号为[21]的馒头
和尚24: 共吃了【1】馒头
和尚23: 吃了编号为[24]的馒头
和尚21: 吃了编号为[27]的馒头
和尚21: 共吃了【1】馒头
和尚23: 共吃了【1】馒头
和尚30: 共吃了【1】馒头
和尚29: 吃了编号为[1]的馒头
和尚29: 共吃了【2】馒头
以上代码输出结果戳这里
三.(熊吃蜂蜜升级版本)两只熊,100只蜜蜂,蜜蜂每次生产的蜂蜜量是1到5不等,罐子的容量是30,熊在罐子的蜂蜜量达到20的时候,一次性将蜂蜜吃光,蜜蜂向罐子中添加尽可能的蜂蜜,如果有剩余的话,下次继续添加剩余的量。
/*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/%E5%B0%8F%E8%AF%95%E7%89%9B%E5%88%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.smallTestBullKnife; import java.util.Random; public class BeeproDemo {
public static void main(String[] args) {
BoxPro box = new BoxPro();
Bear b1 = new Bear(box , "熊大");
Bear b2 = new Bear(box , "熊二");
b1.start();
b2.start();
for(int i = 1 ; i <= 100 ; i ++){
new BeePro( "Bee" + i,box).start();
}
}
} /**
* 罐子
*/
class BoxPro{
//定义管子容量的最大值为30
public static int MAX = 30 ;
//定义当前管子的容量
private int size = 0 ; /**
* 添加蜂蜜方法
*/
public synchronized void add(int cap) {
//判断添加的蜂蜜是否会使得管子变满
if (cap > (MAX - size)){
//获取添加蜂蜜多出的量
int remain = cap + size -MAX;
//如果添加的密封会是管子溢出的话,我们直接把罐子加满即可!
size = MAX;
//加满罐子后通知其他线程来消费,并让当前线程进入等待队列
this.notifyAll();
try {
this.wait();
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
//如果当前线程被唤醒后,我们需要将之前剩余量继续进行添加操作!
add(remain);
}
//如果当前的蜂蜜不能使罐子盛满,我们就直接添加蜂蜜即可!
size+=cap;
//加完之后需要通知其他线程
this.notifyAll();
} /**
* 清空罐子
*/
public synchronized int clear(){
while(size < 20){
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int temp = size ;
size = 0 ;
return temp ;
}
} /**
* 生产者
*/
class BeePro extends Thread{
private String beeName ;
private BoxPro box ;
public BeePro(String name,BoxPro box){
this.beeName = name ;
this.box = box ;
} public void run() {
Random r = new Random();
while(true){
int production =r.nextInt(5) + 1;
box.add(production);
System.out.println(beeName + "生产了" + production +"蜂蜜");
}
}
} /**
* 消费者
*/
class Bear extends Thread{
private String bearName ;
private BoxPro box ;
public Bear(BoxPro box , String name){
this.bearName = name ;
this.box = box ;
} public void run() {
while(true){
int size = box.clear();
System.out.println(bearName + " 吃了【" + size +"】个蜂蜜");
}
}
}
四.两个售票员一起买100000张票,使用两种加锁方式(synchronize | ReentrantLock),看性能比对。
/*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/%E5%B0%8F%E8%AF%95%E7%89%9B%E5%88%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.smallTestBullKnife; import java.util.concurrent.locks.ReentrantLock; public class SaleDemo {
public static void main(String[] args) throws InterruptedException {
TicketPool pool1 = new TicketPool();
Thread[] ts = new Thread[4];
long start = System.currentTimeMillis();
for (int i=0;i<4;i++){
Saler yzj = new Saler("yinzhengjie" + i, pool1, true);
ts[i] = yzj;
yzj.start();
}
for (Thread t : ts) {
t.join();
}
long end = System.currentTimeMillis();
System.out.printf("传统(normal)买票方式用时为:[%d]\n",(end-start)); TicketPool pool2 = new TicketPool();
start = System.currentTimeMillis();
for (int i=0;i<4;i++){
Saler yzj = new Saler("yinzhengjie" + i, pool2, false);
ts[i] = yzj;
yzj.start();
}
for (Thread t : ts) {
t.join();
}
end = System.currentTimeMillis();
System.out.printf("轻量级(light)买票方式用时为:[%d]\n",(end-start)); }
} /**
* 定义票池
*/
class TicketPool{
private int Tickets = 100000; /**
* 定义传统synchronized方式买票
*/
public int doGetTickets1(){
//判断是否符合买票的规则,如果票已经卖完了,就直接返回0.
if (Tickets <= 0){
return 0;
}
//使用synchronized方法同步代码块,保证原子性!
synchronized(this){
//定义当前票数的编号变量temp
int temp = Tickets;
//如果卖出去一张票,就将票数自减1.
Tickets--;
return temp;
}
} ReentrantLock lock = new ReentrantLock();
public int doGetTickets2(){
//判断是否符合买票的规则,如果票已经卖完了,就直接返回0.
if (Tickets <= 0){
return 0;
}
//上锁,知道写锁的代码块,同样也是保证原子性!
lock.lock();
//定义当前票数的编号变量temp
int temp = Tickets;
//如果卖出去一张票,就将票数自减1.
Tickets--;
//解锁
lock.unlock();
return temp;
}
} /**
* 定义售票员类
*/
class Saler extends Thread{
private String saleName;
private TicketPool pool;
private boolean normal;
/**
*
* @param saleName //制定和售票员名称
* @param pool //指定票池
* @param normal //是都使用传统方式买票
*/
public Saler(String saleName,TicketPool pool,boolean normal){
this.saleName = saleName;
this.pool = pool;
this.normal = normal;
} public void run() {
while (true){
int num = normal ? pool.doGetTickets1():pool.doGetTickets2();
if (num == 0){
return;
}
// System.out.printf("售票员[%s]出售了第【%d】票\n",saleName,num); } }
} /*
以上代码输出结果如下:
传统(normal)买票方式用时为:[9]
轻量级(light)买票方式用时为:[8]
*/
五.编写socket通信的MyServer,使用分线程完成和每个client的通信。
/*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/%E5%B0%8F%E8%AF%95%E7%89%9B%E5%88%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.socket; import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket; public class MyServer {
public static void main(String[] args) throws Exception {
//服务器套接字
ServerSocket ss = new ServerSocket(8888) ;
while(true){
//接受连接,
System.out.println("正在接受连接.....");
Socket sock = ss.accept();
new CommThread(sock).start();
}
}
} /**
* 服务器和每个客户端的通信线程
*/
class CommThread extends Thread{
private Socket sock; public CommThread(Socket sock){
this.sock = sock ;
} public void run() {
try {
//获取远程地址和端口
InetSocketAddress addr = (InetSocketAddress) sock.getRemoteSocketAddress();
int port = addr.getPort();
String ip = addr.getAddress().getHostAddress();
System.out.printf("有人连接进来了!! : %s , %d\r\n", ip, port); //向客户端发送消息
int index = 0;
OutputStream out = sock.getOutputStream();
while (true) {
index ++ ;
out.write(("yinzhengjie" + index + "\r\n").getBytes());
out.flush();
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
需要启动上述代码的服务端,才能用客户端进行连接操作
1>.使用java编写的客户端连接服务端
/*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/%E5%B0%8F%E8%AF%95%E7%89%9B%E5%88%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.socket; import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket; public class MyClient {
public static void main(String[] args) throws Exception {
Socket s = new Socket("www.yinzhengjie.org.cn" ,8888) ;
System.out.println("连接到服务器了!!");
InputStream in = s.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in)) ;
String line = null ;
//获取服务端发来的消息
while((line = br.readLine())!= null){
System.out.println("收到消息 : " + line);
}
}
}
MyClient.java 文件内容
测试结果如下:
2>.使用Windows自带的telnet客户端连接服务端
查看接收的数据信息如下:
3>.使用xshell客户端连接服务端
综上所述,有三个端连接了服务端,我们可以查看服务端的输出数据如下:
上述的解决方案当客户端数量过多的话,最终可能会存在资源耗尽的情况,建议使用NIO技术,详情请参考:https://www.cnblogs.com/yinzhengjie/p/9257142.html。
java基础-多线程应用案例展示的更多相关文章
- Java基础-数据类型应用案例展示
Java基础-数据类型应用案例展示 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.把long数据转换成字节数组,把字节数组数据转换成long. /* @author :yinz ...
- Java基础-多线程-③线程同步之synchronized
使用线程同步解决多线程安全问题 上一篇 Java基础-多线程-②多线程的安全问题 中我们说到多线程可能引发的安全问题,原因在于多个线程共享了数据,且一个线程在操作(多为写操作)数据的过程中,另一个线程 ...
- Java基础-多线程-②多线程安全问题
什么是线程的安全问题? 上一篇 Java基础-多线程-①线程的创建和启动 我们说使用实现Runnable接口的方式来创建线程,可以实现多个线程共享资源: class Dog implements Ru ...
- java基础-多线程二
java基础-多线程二 继承thread和实现Runnable的多线程每次都需要经历创建和销毁的过程,频繁的创建和销毁大大影响效率,线程池的诞生就可以很好的解决这一个问题,线程池可以充分的利用线程进行 ...
- 备战金三银四!一线互联网公司java岗面试题整理:Java基础+多线程+集合+JVM合集!
前言 回首来看2020年,真的是印象中过的最快的一年了,真的是时间过的飞快,还没反应过来年就夸完了,相信大家也已经开始上班了!俗话说新年新气象,马上就要到了一年之中最重要的金三银四,之前一直有粉丝要求 ...
- java基础多线程之共享数据
java基础巩固笔记5-多线程之共享数据 线程范围内共享数据 ThreadLocal类 多线程访问共享数据 几种方式 本文主要总结线程共享数据的相关知识,主要包括两方面:一是某个线程内如何共享数据,保 ...
- Java基础——多线程
Java中多线程的应用是非常多的,我们在Java中又该如何去创建线程呢? http://www.jianshu.com/p/40d4c7aebd66 一.常用的有三种方法来创建多线程 新建一个类继承自 ...
- Java基础--多线程的方方面面
1,什么是线程?线程和进程的区别是什么? 2,什么是多线程?为什么设计多线程? 3,Java种多线程的实现方式是什么?有什么区别? 4,线程的状态控制有哪些方法? 5,线程安全.死锁和生产者--消费者 ...
- Java 基础 多线程进阶(锁,线程安全)
一,前言 前面我们已经对线程和线程池有一定的了解,但是只要说到多线程,肯定需要考虑线程安全等问题.接下来我们就来好好聊聊这些问题. 二,线程安全 如果有多个线程在同时运行,而这些线程可能会同时运行这段 ...
随机推荐
- Linux 学习日记 2 (常用命令 + deb包的安装)
常用命令:以下是一些比较常用的命令,主要是关于安装软件的一些命令 @_@ cd ~/下载(文件名)/ //进入这个文件夹 , ~指的是根目录 cd .. //返回上一级文件夹 sudo apt-get ...
- 面向对象第一次练手-------ArrayList集合、类、对象、冒泡排序、类型转换
思维转不过弯儿来 怎么做都是错 哪怕差一个()就成功的事情,也是千差万别 忽然想到一句话:差一步就成功的距离 = 差几万米就成功的距离 部分的理解和都体现在代码和注释里 using S ...
- kubernetes部署mysql
第一章 部署K8S集群 https://www.cnblogs.com/zoulixiang/p/9504324.html 第二章 1.新建mysql-rc.yaml vi mysql-rc.yaml ...
- MOSFET中的重要参数
最近在调试MOSFET电路中,发现了更多问题,比如同样的PI反馈控制电路可以很好的控制PMOS工作,却对NMOS不能很好控制.当然你肯定会说那是因为PMOS和NMOS不同呀,这自然没有错,我在上一篇文 ...
- VC++ MFC程序设置以管理员权限运行
1.引入资源文件 (.manifest文件),文件中的 name 值为程序全称.exe:文件内容如下: <?xml version="1.0" encoding=" ...
- java BigDecimal实现精确加减乘除运算
java.math.BigDecimal.BigDecimal一共有4个够造方法,让我先来看看其中的两种用法: 第一种:BigDecimal(double val)Translates a doubl ...
- 团队作业Week6:规格说明书编写
(1)请分析你们团队项目的典型用户和场景,并写一个团队博客发布你们团队项目的功能规格说明书. (2)再写一个博客团队博客发布你们项目的设计文档(技术规格说明书). 截止时间:2015-11-03
- kafka学习总结之kafka核心
1. Kafka核心组件 (1)replication(副本).partition(分区) 一个topic可以有多个副本,副本的数量决定了有多少个broker存放写入的数据:副本是以partitio ...
- linux 常用命令-变量命令
想要的结果,有时候我们想使用上一句命令的执行结果,当然可以通过鼠标去复制粘贴,但是这样既不库又效率低,所以想能不能通过快捷键获取上一句命令的值执行结果呢,答案是不能,后来想如果能把执行结果存入变量那不 ...
- 代理 ip
利用 VPN 技术通过一台服务器将自己的电脑冒名借用这个服务器的ip ,这台服务器的 ip 即为代理 ip,被冒名ip的服务器即为 代理服务器.我猜的. 实验 这次使用的是 芝麻软件 代理ip软件,其 ...