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

    最近做的偏向并发了,因为以后消息会众多,所以,jms等多个线程操作数据的时候,对共享变量,这些要很注意,以防止发生线程不安全的情况。

    (一)

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

    就像笔者之前做的消息的发送,一个是服务器,一个是客户端。发送的话,要保证信息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方法。

  • 相关阅读:
    Jmeter之http性能测试实战 非GUI模式压测 NON-GUI模式 结果解析TPS——干货(十一)
    UI Recorder 自动化测试 回归原理(九)
    UI Recorder 自动化测试 录制原理(八)
    UI Recorder 自动化测试 整体架构(七)
    UI Recorder 自动化测试 配置项(六)
    UI Recorder 自动化测试 工具栏使用(五)
    UI Recorder 自动化测试 回归测试(四)
    UI Recorder 自动化测试 录制(三)
    UI Recorder 自动化测试工具安装问题疑难杂症解决(二)
    UI Recorder 自动化测试安装教程(一)
  • 原文地址:https://www.cnblogs.com/juepei/p/3926673.html
Copyright © 2011-2022 走看看