会员可以在此提问,百战程序员老师有问必答
对大家有帮助的问答会被标记为“推荐”
看完课程过来浏览一下别人提的问题,会帮你学得更全面
截止目前,同学们一共提了 132837个问题
JAVA 全系列/第二阶段:JAVA 基础深化和提高/容器(旧) 1997楼
JAVA 全系列/第二阶段:JAVA 基础深化和提高/反射技术(旧) 2000楼

老师,一对多聊天服务中,我的客户端不是每次都能返回消息。

下面的是服务端的代码

package com.bjsxt.ls.网络编程.TCP.一对多聊天服务器;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;


//创建服务端接收消息的线程
class ChatReceive extends Thread{
    private Socket ss;
    public ChatReceive(Socket n){
        this.ss=n;
    }

    @Override
    public void run() {
        this.receive1();
    }

    //创建接收消息的线程
    public void receive1(){
        BufferedReader br =null;
        try {
            br = new BufferedReader(new InputStreamReader(this.ss.getInputStream()));
            while (true){
                String jieshou = br.readLine();
                synchronized ("同步锁") {       //要让发送消息和接收消息的线程处于同步状态,需要在两个线程上加上同一个名字的对象锁
                    ChatServer.BUF = jieshou; //将接收到的消息保存到缓存区
                    "同步锁".notify();        //当消息存到缓存区之后,唤醒发送消息线程,可以发送消息了
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (br != null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (this.ss != null){
                try {
                    ss.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

//创建服务端发送消息的线程类
class ChatSend extends Thread{
    private Socket ss;
    public  ChatSend(Socket n){
        this.ss=n;
    }

    @Override
    public void run() {
        this.send1();
    }

    //创建发送消息的方法
    public void send1(){
        PrintWriter pw = null ;
        try {
            pw = new PrintWriter(this.ss.getOutputStream());
            while (true) {
                synchronized ("同步锁") { //要让发送消息和接收消息的线程处于同步状态,需要在两个线程上加上同一个名字的对象锁
                    "同步锁".wait();  //发送消息的线程应先处于等待状态
                    pw.println(ChatServer.BUF); //将消息从缓存区取出,因为BUF是static的,直接用类名就能调用
                    pw.flush();
                }
            }
        }  catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (pw != null){
                pw.close();
            }
            if (this.ss != null){
                try {
                    this.ss.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

//主线程用于启动服务端,客户端可以使用之前的“点对点优化”中的GoodP2P
public class ChatServer {
    //定义一个字符串类型的缓存区
    public static String BUF;
    public static void main(String[] args) {
        System.out.println("正在启动服务器.....");
        System.out.println("<HappyDay>聊天室启动");
        ServerSocket serverSocket = null;
        try {
            serverSocket =new ServerSocket(332);
            while (true){
                Socket sk =serverSocket.accept(); //将启动服务端放在循环中,因为每次发送或接收消息都需要启动服务端
                System.out.println("已经连接到IP地址为["+ InetAddress.getLocalHost() +"]的主机。");
                new ChatReceive(sk).start(); //启动线程
                new ChatSend(sk).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(serverSocket!=null){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

这个是用的优化的点对点聊天作为客户端的代码

package com.bjsxt.ls.网络编程.TCP.点对点优化;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

//创建发送消息线程
class send1 extends Thread{
    private Socket socket =null;
    private Scanner scanner =null;
    public send1(Socket s1,Scanner s2){
        this.socket =s1;
        this.scanner=s2;
    }

    //创建发送消息的方法
    public void sengmessage(){
        PrintWriter pw =null;
        try{
            pw =new PrintWriter(socket.getOutputStream());
            scanner = new Scanner(System.in);
            while (true){
                String say =scanner.nextLine();
                pw.println(say);
                pw.flush();
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (pw != null){
                pw.close();
            }
            if (scanner != null){
                scanner.close();
            }
            if (socket != null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    @Override
    public void run() {
        this.sengmessage();
    }
}

//创建接收消息的线程
class receive extends Thread{
    private Socket socket =null;
    public receive(Socket x){
        this.socket = x;
    }
    public void receivemessage(){
        BufferedReader br= null;
        try {
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            while (true){
                String rs = br.readLine();
                System.out.println("收到的消息是:"+rs);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (br != null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (socket!= null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    @Override
    public void run() {
        this.receivemessage();
    }
}
public class GoodP2P {
    public static void main(String[] args) {
        Scanner scanner = null;
        ServerSocket serverSocket = null;
        Socket socket = null;
        scanner =new Scanner(System.in);
        System.out.println("请输入:server,<port> 或者:" + "<ip>,<port>");
        String str = scanner.nextLine();
        String arr[] =str.split(",");//以逗号为分割点存入数组

        //判断当输入的server时,启动服务端
        if ("server".equals(arr[0])){
            System.out.println("TCP点对点通信,正在启动服务端,连接端口号["+arr[1]+"]");
            try {
                serverSocket =new ServerSocket(Integer.parseInt(arr[1]));//将字符串强转为整型
                socket = serverSocket.accept();
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("连接成功!");
        }
        //当输入的不是server时,即进入客户端
        else {
            try {
                socket = new Socket(arr[0],Integer.parseInt(arr[1]));
                System.out.println("连接成功!");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //启动发送消息的线程
        new send1(socket,scanner).start();
        new receive(socket).start();
        if (serverSocket != null){
            try {
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

下面的运行的截图:

服务端测试


客户端1



客户端2




客户端3




JAVA 全系列/第二阶段:JAVA 基础深化和提高/网络编程(旧) 2001楼

我这个问题是在哪?

/**
 * 账户类
 */
class Account{
    private String accountNum;//账户
    private double balance;//余额

    public Account(){

    }

    public Account(String accountNum, double balance) {
        this.accountNum = accountNum;
        this.balance = balance;
    }

    public String getAccountNum() {
        return accountNum;
    }

    public void setAccountNum(String accountNum) {
        this.accountNum = accountNum;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }
}


class DrawMoney extends Thread{
    private Account account;//账户对象
    private double drawMoney;//取款金额
    public DrawMoney(String name,Account account,double drawMoney){
        super(name);
        this.account=account;
        this.drawMoney=drawMoney;
    }
    /**
     * 取款线程
     */
    @Override
    public void run() {
        if (this.account.getBalance()>=this.drawMoney){
            System.out.println(this.getName()+"取钱成功 "+this.drawMoney);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.account.setBalance(this.account.getBalance()-this.drawMoney);
            System.out.println("\t 余额为:"+this.account.getBalance());
        }else {
            System.out.println(this.getName()+"取钱失败");
        }
    }
}
public class DrawMoneyThread {
    public static void main(String[] args) {
        Account account=new Account("1234",1000);
        new DrawMoney("小王",account,800).start();
        new DrawMoney("李梅",account,500).start();

    }
}

image.png

JAVA 全系列/第二阶段:JAVA 基础深化和提高/多线程技术(旧) 2003楼
JAVA 全系列/第二阶段:JAVA 基础深化和提高/网络编程(旧) 2004楼
JAVA 全系列/第二阶段:JAVA 基础深化和提高/多线程技术(旧) 2006楼
JAVA 全系列/第二阶段:JAVA 基础深化和提高/Lambda表达式(旧) 2007楼

map.get(u)值返回为null,而不是我put的值。但是我用比较器进行比较,结果又是正常的。

public class TreeMapTest {
    public static void main(String[] args) {
        Map<Users,String> map = new TreeMap<>();
        Users u1 = new Users("小a",18);
        Users u2 = new Users("小b",5);
        Users u3 = new Users("小c",9);

        String value = map.put(u1,"a");
        System.out.println(value);
        System.out.println(map.get(u1));
        map.put(u2,"b");
        map.put(u3,"c");

        Set<Users> keys = map.keySet();
        for(Users u: keys){
            System.out.println(u+"--- "+map.get(u));
        }
    }
}


public class Users implements Comparable<Users>{
    private String uname;
    private int uage;

    public String getUname() {
        return uname;
    }

    public void setUname(String uname) {
        this.uname = uname;
    }

    public int getUage() {
        return uage;
    }

    public void setUage(int uage) {
        this.uage = uage;
    }

    public Users(String uname, int uage) {
        this.uname = uname;
        this.uage = uage;
    }

    public Users() {
    }


//如果大于返回1,小于返回-1 ,相等返回0
    @Override
    public int compareTo(Users o) {
        if(this.uage > o.uage){
            return 1;
        }else{
            return -1;
        }
    }

    @Override
    public String toString() {
        return "Users{" +
                "uname='" + uname + '\'' +
                ", uage=" + uage +
                '}';
    }
}

image.png

JAVA 全系列/第二阶段:JAVA 基础深化和提高/容器(旧) 2008楼

package com.bjsxt.ls.DuoXianCheng.线程并发;
/*
线程并发:生产者和消费者模型
定义一个做馒头和取馒头的类,中间有一个缓冲区。做好的馒头放入缓冲区,取馒头从缓冲区取。
假设缓冲区容量是10,那么当做了十个馒头的时候,做馒头线程就要停止(阻塞)。当缓冲区没有馒头的时候,取馒头线程就要停止(阻塞)。
但是当做馒头线程放入馒头的时候,就要用notify提醒取馒头线程,不要一直处于阻塞状态;
同样当取馒头线程拿馒头的时候,也要提醒做馒头线程要做馒头,不要一直处于阻塞状态;
*/

//定义馒头类
class ManTou{
    private int id;
    public ManTou(int id) {
        this.id = id;
    }
    public int getId() {
        return id;
    }
}

//定义缓冲区,用数组来存放馒头
class resitor{
    
    private ManTou[] arr =new ManTou[10]; //创建一个长度为10,类型为ManTou的数组
    private int index; //定义索引

    //定义做馒头方法
    //因为做馒头和取馒头都是对同样对象进行操作,所以这两个状态是要互斥,即同步的。所以要用synchronized使得这两个状态处于同步状态
    //synchronzied放在方法名上相当于将synchronized(this){}将方法体包裹起来。
    public synchronized void makeMantou(ManTou manTou){
        //用while做出判断,如果当数组满的时候,就要用wait方法,使得此线程进入阻塞状态,不再生产馒头了;
        while (this.index == this.arr.length){
            try {
                wait();    /*wait属于Object类只能用在synchronized块中。
                           此方法执行之后,在本方法所在的对象锁会被阻塞, 其他需要该对象锁的线程就可以继续运行了。*/
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }              //notify也属于Object类
        this.notify();//当放入馒头的时候,要用notify唤醒取馒头的线程,以防拿馒头线程处于阻塞状态 。
                     //该方法会唤醒处于等待状态队列中的一个线程
        this.arr[this.index]=manTou;
        index++;
    }

    //定义取馒头方法
    public synchronized ManTou takeMantou(){
        //用while判断,当索引为0,即缓冲区没有馒头的时候,就用wait阻塞此状态,不要再去取馒头了;
        while (this.index==0){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.notify();//拿馒头的时候也要唤醒做馒头的线程,以防处于阻塞状态不做馒头了。
        this.index--;
        return this.arr[this.index];
    }
}


//创建做馒头线程
class makeThread extends Thread{
    private resitor r; //定义一个类型是resitor的变量r
    public makeThread(resitor r){
        this.r = r;
    }

    @Override
    public void run() {
        for (int i=1;i<11;i++){
            System.out.println("生成第"+i+"个馒头");
            ManTou manTou =new ManTou(i);
            this.r.push(manTou);
        }
    }
}

//创建取馒头线程
class takeThread extends Thread{
    private resitor r; //定义一个类型是resitor的变量r
    public takeThread(resitor r){
        this.r = r;
    }

    @Override
    public void run() {
        for (int i=1;i<11;i++){
            ManTou manTou =this.r.pop();
            System.out.println("拿走第"+i+"个馒头");
        }
    }
}

public class ProducerThread {
    public static void main(String[] args) {
        resitor tt =new resitor();
        new makeThread(tt).start();
        new takeThread(tt).start();
    }
}

QQ图片20210425162135.png

老师这是什么原因造成的?

JAVA 全系列/第二阶段:JAVA 基础深化和提高/多线程技术(旧) 2009楼
JAVA 全系列/第二阶段:JAVA 基础深化和提高/多线程技术(旧) 2010楼

课程分类

百战程序员微信公众号

百战程序员微信小程序

©2014-2025百战汇智(北京)科技有限公司 All Rights Reserved 北京亦庄经济开发区科创十四街 赛蒂国际工业园
网站维护:百战汇智(北京)科技有限公司
京公网安备 11011402011233号    京ICP备18060230号-3    营业执照    经营许可证:京B2-20212637