java基础-多线程应用案例展示

                      java基础-多线程应用案例展示

                                        作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

一.两只熊,100只蜜蜂,蜜蜂每次生产的蜂蜜量是1,罐子的容量是30,熊在罐子的蜂蜜量达到20的时候,一次性将蜂蜜吃光。

  1 /*
  2 @author :yinzhengjie
  3 Blog:http://www.cnblogs.com/yinzhengjie/tag/%E5%B0%8F%E8%AF%95%E7%89%9B%E5%88%80/
  4 EMAIL:y1053419035@qq.com
  5 */
  6 package cn.org.yinzhengjie.smallTestBullKnife;
  7 
  8 public class BeeDemo {
  9     public static void main(String[] args) {
 10         Box box = new Box();
 11         Bear b1 = new Bear(box , "熊大");
 12         Bear b2 = new Bear(box , "熊二");
 13         b1.start();
 14         b2.start();
 15         for(int i = 1 ; i <= 100 ; i ++){
 16             new Bee(box , "Bee" + i).start();
 17         }
 18     }
 19 }
 20 
 21 /**
 22  * 罐子
 23  */
 24 class Box{
 25     public static int MAX = 30 ;
 26     private int size = 0 ;
 27 
 28     /**
 29      * 添加蜂蜜方法
 30      */
 31     public synchronized void add(int cap){
 32         while((size + cap) > MAX){
 33             this.notifyAll();
 34             try {
 35                 this.wait();
 36             } catch (InterruptedException e) {
 37                 e.printStackTrace();
 38             }
 39         }
 40         size = size + cap ;
 41         this.notifyAll();
 42     }
 43 
 44     /**
 45      * 清空罐子
 46      */
 47     public synchronized int clear(){
 48         while(size < 20){
 49             this.notifyAll();
 50             try {
 51                 this.wait();
 52             } catch (InterruptedException e) {
 53                 e.printStackTrace();
 54             }
 55         }
 56         int temp = size ;
 57         size = 0 ;
 58         return temp ;
 59     }
 60 }
 61 
 62 /**
 63  * 生产者
 64  */
 65 class Bee extends Thread{
 66     private String beeName ;
 67     private Box box ;
 68     private int  production = 1;
 69     public Bee(Box box ,String name){
 70         this.beeName = name ;
 71         this.box = box ;
 72     }
 73 
 74     public void run() {
 75         while(true){
 76             box.add(production);
 77             System.out.println(beeName + "生产了" + production +"蜂蜜");
 78             try {
 79                 Thread.sleep(50);
 80             } catch (InterruptedException e) {
 81                 e.printStackTrace();
 82             }
 83         }
 84     }
 85 }
 86 
 87 /**
 88  * 消费者
 89  */
 90 class Bear extends Thread{
 91     private String bearName ;
 92     private Box box ;
 93     public Bear(Box box , String name){
 94         this.bearName = name ;
 95         this.box = box ;
 96     }
 97 
 98     public void run() {
 99         while(true){
100 
101             int size = box.clear();
102             System.out.println(bearName + " 吃了【" + size +"】个蜂蜜");
103         }
104     }
105 }

二.有30个和尚,100个馒头,每个和尚最多吃4馒头,最少一个馒头,满足上述条件下,尽快把馒头吃没,使用多线程模拟,和尚就是线程。

 1 /*
 2 @author :yinzhengjie
 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/%E5%B0%8F%E8%AF%95%E7%89%9B%E5%88%80/
 4 EMAIL:y1053419035@qq.com
 5 */
 6 package cn.org.yinzhengjie.smallTestBullKnife;
 7 
 8 /**
 9  * 和尚吃馒头问题
10  */
11 public class MonkDemo {
12     public static void main(String[] args) {
13         Basket b = new Basket();
14         for(int i = 0 ; i < 30 ; i ++){
15             new Monk(b , "和尚" + (i+1)+":	").start();
16         }
17     }
18 }
19 
20 /**
21  * 篮子,100个馒头
22  */
23 class Basket{
24     //馒头数量==编号
25     private int count = 100 ;
26     //和尚数量 ==没吃馒头的和尚数量
27     private int numMonks = 30 ;
28     /**
29      * 获取馒头的方法,返回馒头的编号
30      */
31     public synchronized int getBread(Monk monk){
32         //和尚第一次吃
33         if(monk.count == 0){
34             int temp = count ;
35             count -- ;
36             numMonks -- ;
37             return temp ;
38         }
39         //和尚还可以吃的情况
40         else if(monk.count < Monk.MAX){
41             //判断是否有多余的馒头
42             if(count > numMonks){
43                 int temp = count ;
44                 count -- ;
45                 return temp ;
46             }
47             else{
48                 return -1 ;
49             }
50         }
51         return -1 ;
52     }
53 }
54 
55 /**
56  * 和尚
57  */
58 class Monk extends Thread{
59     private String monkName ;
60     private Basket basket ;
61 
62     //最少一个馒头
63     public static int MIN = 1 ;
64     //最多4个馒头
65     public static int MAX = 4 ;
66 
67     //吃的馒头数
68     public int count ;
69 
70 
71     public Monk(Basket basket , String monkName){
72         this.basket = basket ;
73         this.monkName = monkName ;
74     }
75 
76     public void run() {
77         while(true){
78             int no = basket.getBread(this);
79             if(no ==-1){
80                 break ;
81             }
82             else{
83                 count ++ ;
84                 System.out.println(monkName + "吃了编号为[" + no + "]的馒头");
85             }
86         }
87         System.out.println(monkName + "共吃了【" + count + "】馒头");
88     }
89 }
  1 和尚2:    吃了编号为[100]的馒头
  2 和尚3:    吃了编号为[99]的馒头
  3 和尚3:    吃了编号为[96]的馒头
  4 和尚3:    吃了编号为[95]的馒头
  5 和尚3:    吃了编号为[94]的馒头
  6 和尚3:    共吃了【4】馒头
  7 和尚4:    吃了编号为[97]的馒头
  8 和尚4:    吃了编号为[93]的馒头
  9 和尚4:    吃了编号为[92]的馒头
 10 和尚4:    吃了编号为[91]的馒头
 11 和尚4:    共吃了【4】馒头
 12 和尚2:    吃了编号为[98]的馒头
 13 和尚2:    吃了编号为[90]的馒头
 14 和尚2:    吃了编号为[89]的馒头
 15 和尚2:    共吃了【4】馒头
 16 和尚6:    吃了编号为[88]的馒头
 17 和尚6:    吃了编号为[87]的馒头
 18 和尚6:    吃了编号为[86]的馒头
 19 和尚6:    吃了编号为[85]的馒头
 20 和尚6:    共吃了【4】馒头
 21 和尚1:    吃了编号为[84]的馒头
 22 和尚1:    吃了编号为[82]的馒头
 23 和尚8:    吃了编号为[83]的馒头
 24 和尚8:    吃了编号为[80]的馒头
 25 和尚8:    吃了编号为[79]的馒头
 26 和尚8:    吃了编号为[78]的馒头
 27 和尚8:    共吃了【4】馒头
 28 和尚1:    吃了编号为[81]的馒头
 29 和尚1:    吃了编号为[77]的馒头
 30 和尚1:    共吃了【4】馒头
 31 和尚5:    吃了编号为[76]的馒头
 32 和尚5:    吃了编号为[75]的馒头
 33 和尚12:    吃了编号为[74]的馒头
 34 和尚12:    吃了编号为[71]的馒头
 35 和尚12:    吃了编号为[70]的馒头
 36 和尚12:    吃了编号为[69]的馒头
 37 和尚12:    共吃了【4】馒头
 38 和尚14:    吃了编号为[68]的馒头
 39 和尚14:    吃了编号为[67]的馒头
 40 和尚14:    吃了编号为[66]的馒头
 41 和尚5:    吃了编号为[73]的馒头
 42 和尚5:    吃了编号为[64]的馒头
 43 和尚5:    共吃了【4】馒头
 44 和尚14:    吃了编号为[65]的馒头
 45 和尚14:    共吃了【4】馒头
 46 和尚10:    吃了编号为[72]的馒头
 47 和尚10:    吃了编号为[63]的馒头
 48 和尚10:    吃了编号为[62]的馒头
 49 和尚10:    吃了编号为[61]的馒头
 50 和尚10:    共吃了【4】馒头
 51 和尚9:    吃了编号为[60]的馒头
 52 和尚9:    吃了编号为[59]的馒头
 53 和尚7:    吃了编号为[58]的馒头
 54 和尚7:    吃了编号为[56]的馒头
 55 和尚7:    吃了编号为[55]的馒头
 56 和尚11:    吃了编号为[54]的馒头
 57 和尚11:    吃了编号为[52]的馒头
 58 和尚11:    吃了编号为[51]的馒头
 59 和尚9:    吃了编号为[57]的馒头
 60 和尚9:    吃了编号为[48]的馒头
 61 和尚9:    共吃了【4】馒头
 62 和尚11:    吃了编号为[50]的馒头
 63 和尚11:    共吃了【4】馒头
 64 和尚7:    吃了编号为[53]的馒头
 65 和尚15:    吃了编号为[45]的馒头
 66 和尚15:    吃了编号为[44]的馒头
 67 和尚15:    吃了编号为[42]的馒头
 68 和尚15:    吃了编号为[41]的馒头
 69 和尚15:    共吃了【4】馒头
 70 和尚17:    吃了编号为[46]的馒头
 71 和尚17:    吃了编号为[39]的馒头
 72 和尚19:    吃了编号为[38]的馒头
 73 和尚19:    吃了编号为[36]的馒头
 74 和尚19:    吃了编号为[35]的馒头
 75 和尚19:    吃了编号为[34]的馒头
 76 和尚19:    共吃了【4】馒头
 77 和尚13:    吃了编号为[47]的馒头
 78 和尚13:    吃了编号为[33]的馒头
 79 和尚13:    吃了编号为[32]的馒头
 80 和尚13:    吃了编号为[31]的馒头
 81 和尚13:    共吃了【4】馒头
 82 和尚20:    吃了编号为[49]的馒头
 83 和尚20:    吃了编号为[30]的馒头
 84 和尚20:    吃了编号为[29]的馒头
 85 和尚20:    吃了编号为[28]的馒头
 86 和尚20:    共吃了【4】馒头
 87 和尚22:    吃了编号为[26]的馒头
 88 和尚22:    吃了编号为[25]的馒头
 89 和尚22:    吃了编号为[23]的馒头
 90 和尚22:    吃了编号为[22]的馒头
 91 和尚22:    共吃了【4】馒头
 92 和尚17:    吃了编号为[37]的馒头
 93 和尚17:    吃了编号为[20]的馒头
 94 和尚17:    共吃了【4】馒头
 95 和尚18:    吃了编号为[40]的馒头
 96 和尚18:    吃了编号为[19]的馒头
 97 和尚18:    吃了编号为[18]的馒头
 98 和尚18:    吃了编号为[17]的馒头
 99 和尚18:    共吃了【4】馒头
100 和尚25:    吃了编号为[16]的馒头
101 和尚27:    吃了编号为[15]的馒头
102 和尚27:    吃了编号为[13]的馒头
103 和尚16:    吃了编号为[43]的馒头
104 和尚16:    吃了编号为[11]的馒头
105 和尚16:    吃了编号为[10]的馒头
106 和尚16:    吃了编号为[8]的馒头
107 和尚16:    共吃了【4】馒头
108 和尚26:    吃了编号为[7]的馒头
109 和尚7:    共吃了【4】馒头
110 和尚26:    吃了编号为[6]的馒头
111 和尚26:    吃了编号为[4]的馒头
112 和尚26:    吃了编号为[3]的馒头
113 和尚26:    共吃了【4】馒头
114 和尚29:    吃了编号为[5]的馒头
115 和尚30:    吃了编号为[2]的馒头
116 和尚28:    吃了编号为[9]的馒头
117 和尚28:    共吃了【1】馒头
118 和尚27:    吃了编号为[12]的馒头
119 和尚27:    共吃了【3】馒头
120 和尚25:    吃了编号为[14]的馒头
121 和尚25:    共吃了【2】馒头
122 和尚24:    吃了编号为[21]的馒头
123 和尚24:    共吃了【1】馒头
124 和尚23:    吃了编号为[24]的馒头
125 和尚21:    吃了编号为[27]的馒头
126 和尚21:    共吃了【1】馒头
127 和尚23:    共吃了【1】馒头
128 和尚30:    共吃了【1】馒头
129 和尚29:    吃了编号为[1]的馒头
130 和尚29:    共吃了【2】馒头
以上代码输出结果戳这里

三.(熊吃蜂蜜升级版本)两只熊,100只蜜蜂,蜜蜂每次生产的蜂蜜量是1到5不等,罐子的容量是30,熊在罐子的蜂蜜量达到20的时候,一次性将蜂蜜吃光,蜜蜂向罐子中添加尽可能的蜂蜜,如果有剩余的话,下次继续添加剩余的量。

  1 /*
  2 @author :yinzhengjie
  3 Blog:http://www.cnblogs.com/yinzhengjie/tag/%E5%B0%8F%E8%AF%95%E7%89%9B%E5%88%80/
  4 EMAIL:y1053419035@qq.com
  5 */
  6 package cn.org.yinzhengjie.smallTestBullKnife;
  7 
  8 import java.util.Random;
  9 
 10 public class BeeproDemo {
 11     public static void main(String[] args) {
 12         BoxPro box = new BoxPro();
 13         Bear b1 = new Bear(box , "熊大");
 14         Bear b2 = new Bear(box , "熊二");
 15         b1.start();
 16         b2.start();
 17         for(int i = 1 ; i <= 100 ; i ++){
 18             new BeePro(  "Bee" + i,box).start();
 19         }
 20     }
 21 }
 22 
 23 /**
 24  * 罐子
 25  */
 26 class BoxPro{
 27     //定义管子容量的最大值为30
 28     public static int MAX = 30 ;
 29     //定义当前管子的容量
 30     private int size = 0 ;
 31 
 32     /**
 33      * 添加蜂蜜方法
 34      */
 35     public synchronized void add(int cap) {
 36         //判断添加的蜂蜜是否会使得管子变满
 37         if (cap > (MAX - size)){
 38             //获取添加蜂蜜多出的量
 39             int remain = cap + size -MAX;
 40             //如果添加的密封会是管子溢出的话,我们直接把罐子加满即可!
 41             size = MAX;
 42             //加满罐子后通知其他线程来消费,并让当前线程进入等待队列
 43             this.notifyAll();
 44             try {
 45                 this.wait();
 46                 Thread.sleep(500);
 47             } catch (Exception e) {
 48                 e.printStackTrace();
 49             }
 50             //如果当前线程被唤醒后,我们需要将之前剩余量继续进行添加操作!
 51             add(remain);
 52         }
 53         //如果当前的蜂蜜不能使罐子盛满,我们就直接添加蜂蜜即可!
 54         size+=cap;
 55         //加完之后需要通知其他线程
 56         this.notifyAll();
 57     }
 58 
 59     /**
 60      * 清空罐子
 61      */
 62     public synchronized int clear(){
 63         while(size < 20){
 64             this.notifyAll();
 65             try {
 66                 this.wait();
 67             } catch (InterruptedException e) {
 68                 e.printStackTrace();
 69             }
 70         }
 71         int temp = size ;
 72         size = 0 ;
 73         return temp ;
 74     }
 75 }
 76 
 77 /**
 78  * 生产者
 79  */
 80 class BeePro extends Thread{
 81     private String beeName ;
 82     private BoxPro box ;
 83     public BeePro(String name,BoxPro box){
 84         this.beeName = name ;
 85         this.box = box ;
 86     }
 87 
 88     public void run() {
 89         Random r = new Random();
 90         while(true){
 91             int production =r.nextInt(5) + 1;
 92             box.add(production);
 93             System.out.println(beeName + "生产了" + production +"蜂蜜");
 94         }
 95     }
 96 }
 97 
 98 /**
 99  * 消费者
100  */
101 class Bear extends Thread{
102     private String bearName ;
103     private BoxPro box ;
104     public Bear(BoxPro box , String name){
105         this.bearName = name ;
106         this.box = box ;
107     }
108 
109     public void run() {
110         while(true){
111             int size = box.clear();
112             System.out.println(bearName + " 吃了【" + size +"】个蜂蜜");
113         }
114     }
115 }

四.两个售票员一起买100000张票,使用两种加锁方式(synchronize | ReentrantLock),看性能比对。

  1 /*
  2 @author :yinzhengjie
  3 Blog:http://www.cnblogs.com/yinzhengjie/tag/%E5%B0%8F%E8%AF%95%E7%89%9B%E5%88%80/
  4 EMAIL:y1053419035@qq.com
  5 */
  6 package cn.org.yinzhengjie.smallTestBullKnife;
  7 
  8 import java.util.concurrent.locks.ReentrantLock;
  9 
 10 public class SaleDemo {
 11     public static void main(String[] args) throws InterruptedException {
 12         TicketPool pool1 = new TicketPool();
 13         Thread[] ts = new Thread[4];
 14         long start = System.currentTimeMillis();
 15         for (int i=0;i<4;i++){
 16             Saler yzj = new Saler("yinzhengjie" + i, pool1, true);
 17             ts[i] = yzj;
 18             yzj.start();
 19         }
 20         for (Thread t : ts) {
 21             t.join();
 22         }
 23         long end = System.currentTimeMillis();
 24         System.out.printf("传统(normal)买票方式用时为:[%d]
",(end-start));
 25 
 26 
 27         TicketPool pool2 = new TicketPool();
 28         start = System.currentTimeMillis();
 29         for (int i=0;i<4;i++){
 30             Saler yzj = new Saler("yinzhengjie" + i, pool2, false);
 31             ts[i] = yzj;
 32             yzj.start();
 33         }
 34         for (Thread t : ts) {
 35             t.join();
 36         }
 37         end = System.currentTimeMillis();
 38         System.out.printf("轻量级(light)买票方式用时为:[%d]
",(end-start));
 39 
 40     }
 41 }
 42 
 43 /**
 44  * 定义票池
 45  */
 46 class TicketPool{
 47     private int Tickets = 100000;
 48 
 49 
 50     /**
 51      * 定义传统synchronized方式买票
 52      */
 53     public  int  doGetTickets1(){
 54         //判断是否符合买票的规则,如果票已经卖完了,就直接返回0.
 55         if (Tickets <= 0){
 56             return 0;
 57         }
 58         //使用synchronized方法同步代码块,保证原子性!
 59         synchronized(this){
 60             //定义当前票数的编号变量temp
 61             int temp = Tickets;
 62             //如果卖出去一张票,就将票数自减1.
 63             Tickets--;
 64             return temp;
 65         }
 66     }
 67 
 68     ReentrantLock lock = new ReentrantLock();
 69     public  int  doGetTickets2(){
 70         //判断是否符合买票的规则,如果票已经卖完了,就直接返回0.
 71         if (Tickets <= 0){
 72             return 0;
 73         }
 74         //上锁,知道写锁的代码块,同样也是保证原子性!
 75         lock.lock();
 76         //定义当前票数的编号变量temp
 77         int temp = Tickets;
 78         //如果卖出去一张票,就将票数自减1.
 79         Tickets--;
 80         //解锁
 81         lock.unlock();
 82         return temp;
 83     }
 84 }
 85 
 86 /**
 87  * 定义售票员类
 88  */
 89 class Saler extends Thread{
 90     private String saleName;
 91     private TicketPool pool;
 92     private boolean normal;
 93     /**
 94      *
 95      * @param saleName      //制定和售票员名称
 96      * @param pool          //指定票池
 97      * @param normal        //是都使用传统方式买票
 98      */
 99     public Saler(String saleName,TicketPool pool,boolean normal){
100         this.saleName = saleName;
101         this.pool = pool;
102         this.normal = normal;
103     }
104 
105     public void run() {
106         while (true){
107             int num = normal ? pool.doGetTickets1():pool.doGetTickets2();
108             if (num == 0){
109                 return;
110             }
111 //            System.out.printf("售票员[%s]出售了第【%d】票
",saleName,num);
112 
113         }
114 
115     }
116 }
117 
118 /*
119 以上代码输出结果如下:
120 传统(normal)买票方式用时为:[9]
121 轻量级(light)买票方式用时为:[8]
122  */

 

五.编写socket通信的MyServer,使用分线程完成和每个client的通信。

 1 /*
 2 @author :yinzhengjie
 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/%E5%B0%8F%E8%AF%95%E7%89%9B%E5%88%80/
 4 EMAIL:y1053419035@qq.com
 5 */
 6 package cn.org.yinzhengjie.socket;
 7 
 8 import java.io.OutputStream;
 9 import java.net.InetSocketAddress;
10 import java.net.ServerSocket;
11 import java.net.Socket;
12 
13 public class MyServer {
14     public static void main(String[] args) throws Exception {
15         //服务器套接字
16         ServerSocket ss = new ServerSocket(8888) ;
17         while(true){
18             //接受连接,
19             System.out.println("正在接受连接.....");
20             Socket sock = ss.accept();
21             new CommThread(sock).start();
22         }
23     }
24 }
25 
26 /**
27  * 服务器和每个客户端的通信线程
28  */
29 class CommThread extends Thread{
30     private Socket sock;
31 
32     public CommThread(Socket sock){
33         this.sock = sock ;
34     }
35 
36     public void run() {
37         try {
38             //获取远程地址和端口
39             InetSocketAddress addr = (InetSocketAddress) sock.getRemoteSocketAddress();
40             int port = addr.getPort();
41             String ip = addr.getAddress().getHostAddress();
42             System.out.printf("有人连接进来了!! : %s , %d
", ip, port);
43 
44             //向客户端发送消息
45             int index = 0;
46             OutputStream out = sock.getOutputStream();
47             while (true) {
48                 index ++ ;
49                 out.write(("yinzhengjie" + index + "
").getBytes());
50                 out.flush();
51                 Thread.sleep(1000);
52             }
53         } catch (Exception e) {
54             e.printStackTrace();
55         }
56     }
57 }

  需要启动上述代码的服务端,才能用客户端进行连接操作  

1>.使用java编写的客户端连接服务端

 1 /*
 2 @author :yinzhengjie
 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/%E5%B0%8F%E8%AF%95%E7%89%9B%E5%88%80/
 4 EMAIL:y1053419035@qq.com
 5 */
 6 package cn.org.yinzhengjie.socket;
 7 
 8 import java.io.BufferedReader;
 9 import java.io.InputStream;
10 import java.io.InputStreamReader;
11 import java.net.Socket;
12 
13 public class MyClient {
14     public static void main(String[] args) throws Exception {
15         Socket s = new Socket("www.yinzhengjie.org.cn" ,8888) ;
16         System.out.println("连接到服务器了!!");
17         InputStream in = s.getInputStream();
18         BufferedReader br = new BufferedReader(new InputStreamReader(in)) ;
19         String line = null ;
20         //获取服务端发来的消息
21         while((line = br.readLine())!= null){
22             System.out.println("收到消息 : " + line);
23         }
24     }
25 }
MyClient.java 文件内容

  测试结果如下:

  2>.使用Windows自带的telnet客户端连接服务端

 

  查看接收的数据信息如下:

  3>.使用xshell客户端连接服务端

  综上所述,有三个端连接了服务端,我们可以查看服务端的输出数据如下:

   上述的解决方案当客户端数量过多的话,最终可能会存在资源耗尽的情况,建议使用NIO技术,详情请参考:https://www.cnblogs.com/yinzhengjie/p/9257142.html。

原文地址:https://www.cnblogs.com/yinzhengjie/p/9255629.html