网络作业内容很容易, 因为所有的网络运作底层细节已经由java.net函数库处理了. 然后使用 串流(上一章内容)

来接收消息, java 串流一般不管上游文件来自哪里, 网络还是本地的txt文件

client 端

- 连接,传送,接收

  1. 如果建立客户端与服务器之间的初始连接

  2. 传送消息到服务器

  3. 如何接收来自服务器的消息

 head first java ( 15 章 )_java

  1. 建立 socket 连接 ( 要知道服务器 ip 和 port )

  Socket chatSocket = new Socket(“192.168.0.99”, 5000);

  注: 不同程序不能共享一个端口

  -- 从服务器读内容

  InputStreamReader stream = new InputStreamReader(chatSocket.getInputStream()); ( 中间层 )

  BufferReader reader = new BufferReader(stream); // 这的 stream 来自于中间层

  String message = reader.readLine();

  head first java ( 15 章 )_服务器_02

  -- 写内容到服务器(也要先建立socket连接)

  head first java ( 15 章 )_服务器_03 

  head first java ( 15 章 )_java_04

- 例子

head first java ( 15 章 )_.net_05head first java ( 15 章 )_服务器_06
Client/* 
 * File: DailyAdviceClient.java
 * -------------------------------
 * socket, test.
 */

import java.net.*;
import java.io.*;

public class DailyAdviceClient {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		DailyAdviceClient client = new DailyAdviceClient();
		client.go();
	}

	
	public void go() {
		try {
			Socket s = new Socket("127.0.0.1", 4242);
			
			InputStreamReader streamReader = new InputStreamReader(s.getInputStream());
			BufferedReader reader = new BufferedReader(streamReader);
			
			String advice = reader.readLine();
			System.out.println("Today you should: " + advice);
			
			reader.close();
		} catch (IOException ex) {
			ex.printStackTrace();
		}
	}
}

 


Server 端

-- 监听端口

head first java ( 15 章 )_线程调度_07

-- client 请求连接

head first java ( 15 章 )_数据_08

-- 建立连接

head first java ( 15 章 )_服务器_09

- 例子

head first java ( 15 章 )_.net_05head first java ( 15 章 )_服务器_06
Server
import java.io.*;
import java.net.*;


public class DailyAdviceServer {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		DailyAdviceServer server = new DailyAdviceServer();
		server.go();
	}
	
	String[] adviceList = { 
			"Take smaller bites", "Go for the tight jeans. No they do not make you look fat",
			"One word: inappria", "Just for today, be honest. Tell your boss what you",
			"You might want to rethink that haircut" 
	};
	
	public void go() {
		try {
			ServerSocket serverSock = new ServerSocket(4242);
			while (true) {
				// all the time wait to accept
				Socket sock = serverSock.accept();
				
				PrintWriter writer = new PrintWriter(sock.getOutputStream());
				String advice = getAdvice();
				writer.println(advice);
				writer.close();
				System.out.println(advice);
			}
		} catch (IOException ex) {
			ex.printStackTrace();
		}
	
	}
	
	private String getAdvice() {
		int random = (int) (Math.random() * adviceList.length);
		return adviceList[random];
	}
	

}


注: 以上 Server 与 Client 都要一起同时使用.

目前的程序是, 当处理一个客户端请求时没有办法处理别的请求, 怎么办呢?

可以通过线程来满足需要

引入线程, 线程是独立的线程, 它代表独立的执行空间, 当你需要启动新的线程时就建立Thread的实例


进入线程的世界

head first java ( 15 章 )_java_12

要记得, java也只是个在底层操作系统上执行的进程,一旦轮到JAVA执行的时候,JAVA虚拟机实际上会执行什么?

哪个字节码会被执行? 答案是目前执行空间最上面的会被执行,在100个毫秒内,目前执行程序代码会被切换到不同空间上

的不同方法。 -- 线程要记录的一项事务时目前线程执行空间做到哪里。

head first java ( 15 章 )_.net_13

- 如何启动新的线程

head first java ( 15 章 )_线程调度_14

head first java ( 15 章 )_数据_15

head first java ( 15 章 )_.net_16

注: 每个 Thread 需要一个任务来执行, 一个可以放在执行空间的任务

对Thread而言, 它是个工人,而 Runnable 就是这个工人的工作。( Runnable带有会放在执行空间的第一项方法 run() )

- 新建立线程的 3 个状态

  head first java ( 15 章 )_java_17

  通常线程会在可执行与执行中两种状态中来回交替,线程有可能会暂时被挡住,例如调用某个被锁住的对象的方法,此时

  线程就的等到锁住对象的线程放开这个对象才能继续执行,这类型的条件会导致线程暂时失效。(由线程调度器控制)

- 线程调度器

  线程调度器会决定哪个线程从等待状态中被挑出来运行,以及何时把哪个线程送回等待被执行的状态。你无法控制调度器

  注: 一旦启动线程, 程序的顺序就不受控制,并不是线程执行完了才执行线程下边的语句,记住,它们是独立的执行空间

  一个线程只能执行一项任务. 一旦开始执行,就不能再接其他任务。

- Thread.sleep(2000)

  如果想要确保其他的线程有机会执行的话,就线程放进睡眠状态。当线程醒来时,它会进入可执行状态等待被调度器挑出来执行。

  try {

        Thread.sleep(2000);

  } catch ( InterruptedException ex) {

      ex.printStackTrace();

  }

  千万不要依靠这种机制来精确地控制执行时机. ( 例如电影中的独白与动作同步 )

- 线程的缺点

  线程会产生并发性的问题( 两个或两个以上的线程存取单一对象的数据. 也就是说 两个或两个以上不同执行空间上的方法都在

  堆上对同一个对象执行 getter 或 setter

  引入 “锁” 原子性

  synchronized的方法,只能被单一线程调用 synchronized , 例如 private synchronized

  锁不是配在方法上的,而是配在对象上,如果对象有两个同步化方法时(即两个synchronized方法),要么都可以进去,要么都不可以。

  同步化方法的目标是要保护重要的数据,但是要记住,你锁住的不是数据而是存取数据的方法。

  为什么锁的不是方法是对象呢, 其主要目的是锁住实例变量, 假如有两个方法非别是a,b, 都对实例变量age进行访问, 判断age的大小以进行操作, 如果只锁住一个函数, 那么其他的函数修改了age 的值, 那么程序就会有问题, 所以要锁住对象.

  每个 java 对象都有一个锁,类也有锁,这表示3个Dog对象在堆上,那么实际有4个锁

  所以线程在开始执行并遇上有同步化的方法时候会发生什么事? 线程会认知到它需要对象的钥匙才能进入该方法,它会取得钥匙

  这是由Java虚拟机来处理的, 没有存取对象锁得API, 如果可以拿到钥匙才会进入方法.

  注: 同步化方法越少越好, 而且范围越小越好, 只要考虑原子性问题的时候, 才考虑同步化

  同步化一定要注意,会引起“死锁”

  head first java ( 15 章 )_线程调度_18

  foo 的到了对象A的锁,同时想申请对象B的锁,同时 bar得到了对象B的锁,想申请对象A的锁,那么就只有等待。。。