HeadFirstJava——13_网络与线程

来源:互联网 发布:bit.edu.cn域名代表 编辑:程序博客网 时间:2024/05/24 00:59

网络

客户端与服务器的应用程序通过socket连接进行通信;

socket代表两个应用程序之间的连接,它们

一、客户端程序

1 客户端工作方式

1.1 连接——如何建立客户端与服务器之间的初始连接?

用户通过建立socket连接来连接服务器;

1.2 传送——如何传达信息到服务器?

用户送出信息给服务器;

1.3 接收——如何接受来自服务器的信息?

用户从服务器接受信息;


2 建立socket连接

创建socket连接必须知道服务器的两个信息——IP地址和端口号;

socket连接的建立代表两台机器之间存有对方的信息,包括网络地址和TCP的端口号;

Socket socket = new Soccket("<IP地址>", <端口号>);

如下,客户端发起socket连接,对应服务器接收socket连接并存储客户端的IP地址和端口号;

Socket socket = new Socket("196.164.1.103", 5000);

注意:

TCP端口只是一个16位宽、用以识别服务器上特定程序的数字;

一个地址可以有65535个不同的端口号可用,其中0~1023的TCP端口号是保留给已知的特定服务使用,1024~65535之间可任意挑选;

网页服务器HTTP端口号——80;HTTPS端口号——443;

POP3邮件服务器端口号——110;SMTP邮局交换服务器端口号——25;

FTP服务器端口号——20;Telnet服务器端口号——23;

Time服务器端口号——37;

如果没有端口号,服务器就无法分辨客户端连接哪个应用程序的服务;


3 从socket上读取数据——使用BufferedReader

Client <-- BufferedReader(缓冲区的字符) <-- InputStreamReader(转换成字符) <-- socket输入流(来自服务器的字节) <-- Server

a 建立对服务器的Socket连接

Socket socket = new Socket("127.0.0.1", 5000);

b 建立连接到Socket上底层输入串流的InputStreamReader

InputStreamReader是个底层与高层串流的桥梁,socket.getInputStream()从socket取得输入串流;

InputStreamReader stream = new InputStreamReader(socket.getInputStream());

c 建立BufferedReader来读取

将BufferedReader链接到InputStreamReader;

BufferedReader reader = new BufferedReader(stream);String message = reader.readLine();


4 写数据到socket上——使用PrintWriter

Client --> PrintWriter --> socket输出流 --> Server

a 对服务器建立socket连接

Socket socket = new Socket("127.0.0.1", 5000);

b 建立链接到Socket的PrintWriter

PkrintWriter是字符数据和字节之间的转换桥梁,可衔接String和Socket两端;

PrintWriter writer = new PrintWriter(socket.getOutputStream());

c 写入数据

println()会在送出的数据后面加上换行;

print()不会加上换行;

writer.println("Message to send.");writer.print("Another message.");


二、服务器程序

1 服务器工作方式

1.1 服务器应用程序对特定端口创建出ServerSocket

服务器应用程序监听4242端口的客户端请求;


ServerSocket serversocket = new ServerSocket(4242);

1.2 客户端对服务器应用程序建立socket连接

客户端事先知道服务器应用程序的IP地址和端口号;


Socket socket = new Socket("190.165.1.103", 4242);

1.3 服务器创建与客户端通信的新socket

accept()方法会在等待用户的socket连接时闲置着;当用户连接上,此方法会返回一个Socket(在不同的端口号),以便于客户端通信;

Socket和ServerSocket的端口不通,因此ServerSocket可空出来等待其他用户;


Socket socket = serversocket.accept();




线程

一、线程与Thread概念

1 线程

线程是独立的线程,代表独立的执行空间;

每个Java应用程序会启动一个主线程——将main()方法放在自己执行空间的最开始处;

2 Thread

Thread是Java中表示线程的类,有启动、连接、闲置线程的方法;

要建立线程就得创建Thread,Thread对象代表线程;当需要新的线程时建立Thread对象;

3 执行空间

当有超过一个以上的执行空间,看起来像是好几件事情同时发生;

实际上只有真正的多处理器系统能同时执行好几件事情;

但使用Java的线程让其看似同时在执行中,即执行动作可在执行空间飞快地来回交换;


二、启动线程

a 建立Runnable对象(线程的任务)

编写实现Runnable的类,即对线程要执行的任务的定义;

Runnable threadJob = new MyRunnable();

b 建立Thread对象(执行工人)并赋值Runnable(任务)

把Runnable对象传给Thread的构造函数,告诉Thread对象把哪个方法放在执行空间去运行;

Thread myThread = new Thread(threadJob);

c 启动Thread

新的线程启动后,把Runnable对象的方法放到新的执行空间中;

myThread.start();

注意:每个Thread需要一个任务来执行。


三、Runnable接口

1 Runnable接口只有一个方法——public void run();

run()方法是新线程所执行的第一项方法;

2 实现Runnable接口

public class MyRunnable implements Runnable{// 将要运行的程序放在这里public void run(){go();}public void go(){doMore();}public void doMore(){System.out.println("top o' the stack");}}calss ThreadTeater{public static void main(String[] args){Runnable threadJob = new MyRunnable();Thread myThread = new Thread(threadJob);// 调用start()让线程开始执行// 在此之前,只是个Thread实例,不是真正的线程myThread.start();System.out.println("back in main");}}


四、线程状态

新建

Thread实例已创建,但还未启动;

Thread t = new Thread(r);

1 可执行

启动线程时,变成可执行状态,表示准备好要执行,只要轮到它就开始;

建立新的执行空间,处于可执行状态等待被跳出来执行;

t.start();

2 执行中

哪个线程执行由Java虚拟机的线程调度机制决定;

3 暂时不可执行(被堵塞状态)

调度器有时会将线程移出可执行状态,或线程本身的程序会sleep(),或线程调用某个被锁住的对象上的方法(等待锁住该对象的线程释放该对象才能继续);


五、线程调度器

线程调度器决定哪个线程从等待状况中被调出来运行,以及何时把哪个线程送回等待被执行的状态;

程序员没有API来控制调度器;


六、使用Thread.sleep()

调用Thread.sleep()方法强迫某线程离开执行中的状态;

该方法可能会抛出InterruptedException异常,因此对其调用必须包在try/catch块中或声明出来;


七、同步化

1 线程缺点

两个及以上的线程存取单一对象的数据,即两个不同执行空间上的方法都在堆上对同一个对象执行getter或setter;

可能会产生并发性问题,导致数据损毁;

public static void main(String[] args){// 将任务初始化RyanAndMonicaJob theJob = new RyanAndMonicaJob();// 创建出使用相同任务的两个线程,代表两个线程都会存取同一个账户Thread one = new Thread(theJob);Thread two = new Thread(theJob);one.setName("Ryan");two.setName("Monica");one.start();two.start();}

2 解决办法——同步化

要保护数据,就使用synchronized关键字修饰方法使其每次只能被单一的线程存取;

synchronized代表线程需要一把钥匙来存取被同步化的线程;

同步化的意义在于指定某段工作要在不能分割的状态下执行;

对象的锁只会在同步化的方法上其作用;

锁不是配在方法上,而是在对象上;

每个Java对象都有一个锁,每个锁只有一把钥匙;

有时,不需要对整个方法同步化,可对部分语句块进行同步化;

public void go(){doStuff();synchronized(this){criticalStuff();moreCriticalStuff();}}

3 同步化缺点——死锁

死锁发生是因为两个线程互相持有对象正在等待的东西,没有方法可以脱离这个情况,因此两个线程一直等下去;

Java没有处理死锁的机制;



0 0
原创粉丝点击