zoukankan      html  css  js  c++  java
  • java多线程 —— 两种实际应用场景模拟

    java多线程-两种实际应用场景模拟 转自 薛定谔的猫

    (一)

    先说说第一个,模拟对信息的发送和接收。场景是这样的:

    就像笔者之前做的消息的发送,一个是服务器,一个是客户端。发送的话,要保证信息100%的发送给客户端,那么发给客户端之后,客户端返回一个消息告诉服务器,已经收到。当服务器一直没有收到客户端返回的消息,那么服务器会一直发送这个信息,直到客户端发送回确认信息,这时候再删除重复发送的这个信息。

    为了模拟这个场景,这里写两个线程,一个是发送,一个是接收,把发送的信息,要保存到线程安全的对象里面,防止发生线程安全问题,这里采用concurrenthashmap。

    发送代码:

    复制代码
    package com.TestThread;
    /*
     * 
     * @author 薛定饿的猫
     * 
     * */
    import java.util.Map.Entry;
    import java.util.concurrent.ConcurrentHashMap;
    
    public class PushThread extends Thread {
    
        @Override
        public void run() {
            // TODO Auto-generated method stub
            try {
                sleep(6000);
                while(MainThread.pushmessage.size()>0){
                    //重发消息
                    for(Entry<Integer, String> hashMap:MainThread.pushmessage.entrySet()){
                        System.out.println("消息id:"+hashMap.getKey()+"未发送成功,在此重发:"+hashMap.getValue());
                    }
                    sleep(1000);
                }
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
    }
    复制代码

    发送代码,是不断遍历内存对象councurrenthashmap,从中取出信息,不断的重发。其中MainThread.pushmessage是内存对象,在最后一段代码中有定义。

    当确认接收到信息后,另外一个线程来删除内存对象。

    删除的代码:

    复制代码
    package com.TestThread;
    
    /*
     * 
     * @author 薛定饿的猫
     * 
     * */
    import java.util.Map.Entry;
    
    public class RemoveThread extends Thread {
    
        @Override
        public void run() {
            // TODO Auto-generated method stub
            try {
                for (int i = 0; i < 10000; i++) {
                    sleep(2000);
                    for(Entry<Integer, String> map:MainThread.pushmessage.entrySet()){
                        if (map.getKey()==i) {
                            System.out.println("成功收到id为:"+map.getKey()+"返回的信息,删除该元素");
                            MainThread.pushmessage.remove(map.getKey());
                        }
                    }
                    System.out.println("内存对象中的元素数量为:"+MainThread.pushmessage.size());
                }
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
    }
    复制代码

    这里是来删除已收到的信息,然后从内存中删除,不再发送。

    然后写一个主类入口:

    复制代码
    package com.TestThread;
    /*
     * 
     * @author 薛定饿的猫
     * 
     * */
    import java.util.concurrent.ConcurrentHashMap;
    
    public class MainThread {
        public static ConcurrentHashMap<Integer, String> pushmessage=new ConcurrentHashMap<Integer,String>();
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                pushmessage.put(i, "该消息是id为"+i+"的消息");
            }
            Thread pushThread=new PushThread();
            Thread remove=new RemoveThread();
            pushThread.start();
            remove.start();
            for (int i = 10; i < 20; i++) {
                pushmessage.put(i, "又一波到来,消息是id为"+i+"的消息");
            }
        }
    }
    复制代码

    这样两个线程可以轮流的进行各自的事情,并且不会造成数据安全的问题。用这种方式,再结合Androidpn的推送机制,会更加符合实际生产中的应用。

    (二)多线程同步计数器

    多线程同步计数器,按道理也是可以按照上面的方式来进行处理,定义一个像concurrenthashmap的变量。在java中,确实也有另外一种变量,原子变量Atomic,有AtomicLong,AtomicInteger,AtomicReference这些。

    如果在多线程环境下要给一些值赋唯一id的话,这个时候,就要考虑这个id的安全性问题,也就是一致性的问题,不能重复。这里有两个实现的代码:

    复制代码
    package com.test;
    
    public class ThreadCount {
        public static void main(String[] args) {
            
            Thread[] threads=new Thread[10000];
            for (int i = 0; i < 10000; i++) {
                threads[i]=new AThread();
                threads[i].start();
            }
        }
    }
    
    class AThread extends Thread{
    
        @Override
        public void run() {
            // TODO Auto-generated method stub
            @SuppressWarnings("unused")
            Counter counter=new Counter();
            System.out.println(Counter.calNum());
        }
        
    }
    
    class Counter{
         private static long num;
         public Counter(){
             synchronized (Counter.class) {
                num++;
            }
         }
         public static synchronized long calNum(){
             return num;
         }
    }
    复制代码

    这里创建了10000个线程,每个线程都来访问这个计数器,在构造方法中来进行值的递增。

    在计数器中,有两次用到同步,很多人都说用同步,经常会对性能造成影响。于是,用第二种的原子变量,这个性能应该会更好。

    代码:

    复制代码
    package com.test;
    
    import java.util.concurrent.atomic.AtomicLong;
    
    public class ThreadCount {
        public static void main(String[] args) {
            
            Thread[] threads=new Thread[10000];
            for (int i = 0; i < 10000; i++) {
                threads[i]=new AThread();
                threads[i].start();
            }
        }
    }
    
    class AThread extends Thread{
    
        @Override
        public void run() {
            System.out.println(MyCounter.calNum());
        }
        
    }
    
    class Counter{
         private static long num;
         public Counter(){
             synchronized (Counter.class) {
                num++;
            }
         }
         public static synchronized long calNum(){
             return num;
         }
    }
    
    class MyCounter{
        private static AtomicLong num=new AtomicLong();
        
        public static synchronized long calNum(){
            return num.incrementAndGet();
        }
    }
    复制代码


    这样写的话,在调用这个计数器的时候,直接不需要再new一个MyCounter对象。

    这样可以作为工具类,直接调用MyCounter的calNum方法。

  • 相关阅读:
    DevOps Workshop 研发运维一体化(北京第二场) 2016.04.27
    DevOps Workshop 研发运维一体化(成都站) 2016.05.08
    TFS 2015 生成不输出任何结果
    挂起的更改中的“解析”是什么意思?原来是微软错误的翻译
    微软研发流程(ALM)管理培训会议(比亚迪汽车)
    TFS实战培训
    【实战TFS】【QQ群】了解别人是如何使用TFS的
    DevOps Workshop 研发运维一体化(广州站)
    制作Visual Studio 2017 (VS 2017) 离线安装包
    在每页(分页)报表中重复显示标题
  • 原文地址:https://www.cnblogs.com/yangyudexiaobai/p/4398887.html
Copyright © 2011-2022 走看看