关于多线程:
多线程的意思其实就是可以同时执行多个程序,并且互相之间不受影响;不过先后顺序都不确定;
单线程的缺点:虽然可以用while循环保证服务端可以一直服务,但是每一次只能服务一个用户,且read()方法是阻塞方法,如果这个已建立连接的用户一直没有发消息,那么连接就会一直阻塞在这,直到用户发消息,才会往下走,而只有这个连接走完了才会进入下一次连接;
并且,如果某一次连接出现异常,会中断整个服务端;
有一个接口Runnable;
public interface Runnable{
public void run(){
}//Thread会自动调用这个方法
}
有一个类Thread;
public class Thread{
private Runnable r;
public Thread(Runnable r){
this.r = r;
}
public void start(){
r.run();
}
}
多线程实际上调用的就是Thread类里的start方法;
而这个方法需要一个r类型的对象来调用它的run方法,所以想要实现多线程就要有一个类实现Runnable方法,并且实现run这个方法,然后new一个Thread对象,调用它的有参构造方法,传入这个实现类的对象,再让Thread对象调用自身的Start方法即可;
简单案例:
//demo1这个类是Runnable的实现类,并且实现了run这个方法;
public class Demo1 implements Runnable {
private String name;
public Demo1(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 6; i++) {
System.out.println("啊....."+name);
//run方法中是不能传参数的,为了每个线程执行时可以打出来的信息不一样,我们可以
//给demo1这个类写成员变量,然后在run的实现逻辑中与这个成员变量关联起来;
}
}
}
//new几个Demo1的对象
public class Demo1Test {
public static void main(String[] args) {
Demo1 demo11 = new Demo1("张三");
Demo1 demo12 = new Demo1("李四");
Demo1 demo13 = new Demo1("王五");
//只要在Demo1的构造方法中传不一样的成员变量值就可以了;
Demo2 demo2 = new Demo2();
//demo1.run(); // 这样调,只是用单线程普通地执行一下这个run方法而已
// 构造一个线程,指定要执行的逻辑
Thread thread1 = new Thread(demo11);//Thread()括号里只能传runnable对象;
Thread thread2 = new Thread(demo12);
Thread thread3 = new Thread(demo13);
// 这种调用,只是按顺序进行普通的方法调用,是在一个单线程中挨个执行的
/*thread1.run();
thread2.run();
thread3.run();
thread4.run()*/
// 将这3个线程以多线程的方式同时运行
thread1.start();
thread2.start();
thread3.start();
//运行结果是每一个线程都是同时执行的,且每一次执行顺序都不一样;
}
}
复杂案例:与socket编程结合;
//为了让服务端可以多线程服务,也就是说一次可以服务多个用户;并且每个线程不会相互影响
public class Talk implements Runnable {
Socket sc;
//将Socket作为Talk的成员变量,这样每次都可以通过构造方法出入不一样的连接,而每一个不一//样的连接也就代表了每一个不一样的客户端;
public Talk(Socket sc) {
this.sc = sc;
}
@Override
public void run() {
try {
//因为输入输出流你会抛异常,而我们不能将这个异常往上抛给run方法,因为run方法中没有抛异常,如果写了就会报错;
// 从连接中获取输入、输出流
InputStream in = sc.getInputStream();
OutputStream out = sc.getOutputStream();
//收第一个问题
byte[] b = new byte[1024];
int num = in.read(b);
System.out.println("收到客户端的问题1:" + new String(b,0,num));
// 回复第一个问题
out.write("我是宇宙无敌超级美少女战士".getBytes());
// 接收第二个问题
num = in.read(b);
System.out.println("收到客户端的问题2:" + new String(b,0,num));
// 回复第二个问题
out.write("我18岁".getBytes());
//每一次用完都要关流,关连接,这是在关掉与联系完了用户之间的连接
in.close();
out.close();
sc.close();
} catch (Exception e) {
System.out.println("发生异常了.......");
}
}
}
//
public class ThreadServerDemo {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(10000);//申请一个端口号
int i=1;
while (true) {
//用while循环保证服务端一直在服务,并且每一次都会接收到新的连接
Socket sc = ss.accept();//接收到连接即可进行下一步,否则一直阻塞不运行;
System.out.println("收到连接"+i);
//连接之后的代码里已没有read()方法,所以不会再发生阻塞,只要接收到了连接就会走下一步;
//每次连接成功后都会创建一个talk对象,并且传入一个连接对象作为该对象的成员变量//,这样就可以实现每个talk对象的特殊化了,因为每个talk都在与不一样的用户服务;
Talk talk = new Talk(sc);
//每建立一次连接都会创建一个线程,这个线程只为这个连接服务,出现异常也不会影响//下一个线程
new Thread(talk).start();
i++;
}
}
}
//总结,这个案例中,每一次收到连接,就会有一个线程去为这个连接服务,而服务端可以迅速进行下一次循环;
另外,每一个main方法也是一个线程,如果想让线程终止,可以用thread.sleep();括号里填毫秒数;那么线程就将会停止相应的时间再接着运行;