V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
QBugHunter
V2EX  ›  Android

关于空引用的问题

  •  
  •   QBugHunter · 2020-12-25 20:15:37 +08:00 · 8312 次点击
    这是一个创建于 1427 天前的主题,其中的信息可能已经有所发展或是发生改变。

    这里有个套接字类,用于发送消息,我使用了一个 List 来储存消息,然后用一个无线循环来发送消息,首先每个消息都设为一个类

    public class SocketMsg{
        public String ip;
        public int port;
        public byte[] msg;
    };
    

    然后我在某个单例类里有一个 List<SocketMsg>来储存要发送的消息,该类由一个函数,添加消息,整个程序都会用通过这个函数添加消息,类似下面的代码

    public MyUdpSocket{
        private MyUdpSocket mySocket = null;
        public static MyUdpSocket getInstance(){
            if(mySocket == null)
                mySocket = new MyUdpSocket();
        };
        
        private List<SocketMsg> msgQueue = new AtrrayList<>();
        
        public addMsgToQueue(SocketMsg msg){
            msgQueque.add(msg);
        };
    };
    

    然后这个 MyUdpSocket 类里还有个内部类,该类继承 Thread,类似下面代码

    private class SendThread extends Thread {
        private SendThread() {
        }
        @Override
        public void run() {
            while (true) {
            if (udpSocket == null || udpSocket.isClosed()) {return;}
            if(msgQueue.size() == 0) continue;
             try {
                 SendMsg sendMsg = msgQueue.get(0);
                 InetAddress devAddress = InetAddress.getByName(sendMsg.IP);
                 DatagramPacket mDatagram = new DatagramPacket(sendMsg.Msg, sendMsg.Msg.length, devAddress, sendMsg.Port);
                 udpSocket.send(mDatagram);
                 msgQueue.remove(0);  //唯一的 remove
                 } catch (Exception e) {
                        Log.d("sendMsg", "--消息发送失败--" + e);
                 }
            }
        }
    }
    

    首先,msgQueue 唯一的一个 remove()就在此处,程序的其他地方只有 add(),然后这个队列我采取发送第一个,然后删除第一个的方式

    然后,程序随机的会报异常 java.lang.NullPointerException: Attempt to read from field 'java.lang.String com.wawa.MySocket.SendMsg.IP' on a null object reference

    注意合格异常是随机的,大概 3-5 条消息,就会报一条

    我想问下,这个大概是那里错了,我每次发送前都会检查 msgQueue 的 size(),如果为 0 就 continue,而且我反复确认,程序里 msgQueue 只有唯一的一处 remove(),我想问下,这个一般是什么情况

    8 条回复    2020-12-26 23:45:43 +08:00
    guchengyehai1
        1
    guchengyehai1  
       2020-12-25 20:35:21 +08:00 via Android
    Queue 非线程安全容器
    LGA1150
        2
    LGA1150  
       2020-12-25 20:39:02 +08:00 via Android
    List 非线程安全
    QBugHunter
        3
    QBugHunter  
    OP
       2020-12-25 20:41:32 +08:00
    @guchengyehai1
    我用的是 List


    @LGA1150
    但其他线程只会 add(),整个程序唯一的一个 remove()就是我示例代码里的,这个我和同事都反复确认过了
    LGA1150
        4
    LGA1150  
       2020-12-25 20:46:44 +08:00 via Android
    @QBugHunter 如果把 ArrayList 换成 Vector 还会出错吗?
    kx5d62Jn1J9MjoXP
        5
    kx5d62Jn1J9MjoXP  
       2020-12-25 21:00:01 +08:00 via Android
    @QBugHunter 两个线程同时 add,会把 msg 加到 arraylist 同一位,两个都对 size 加 1,导致有一位上的 msg 为 null
    Lemeng
        6
    Lemeng  
       2020-12-25 23:54:59 +08:00
    非线程
    liufish
        7
    liufish  
       2020-12-26 15:20:23 +08:00
    子线程直接死循环有点太猛了。
    List 换成 BlockingQueue,按生产-消费模式来做试试?
    twoyuan
        8
    twoyuan  
       2020-12-26 23:45:43 +08:00
    因为 msgQueue.size() 和下面的 msgQueue.get(0) 不是原子化操作,也就是上面说的线程不安全,多线程环境下 size() 后 get() 之前发生了 remove
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5128 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 82ms · UTC 01:17 · PVG 09:17 · LAX 17:17 · JFK 20:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.