网络作业内容很容易, 因为所有的网络运作底层细节已经由java.net函数库处理了. 然后使用 串流(上一章内容)
来接收消息, java 串流一般不管上游文件来自哪里, 网络还是本地的txt文件
client 端
- 连接,传送,接收
1. 如果建立客户端与服务器之间的初始连接
2. 传送消息到服务器
3. 如何接收来自服务器的消息
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();
-- 写内容到服务器(也要先建立socket连接)
- 例子
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 端
-- 监听端口
-- client 请求连接
-- 建立连接
- 例子
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的实例
进入线程的世界
要记得, java也只是个在底层操作系统上执行的进程,一旦轮到JAVA执行的时候,JAVA虚拟机实际上会执行什么?
哪个字节码会被执行? 答案是目前执行空间最上面的会被执行,在100个毫秒内,目前执行程序代码会被切换到不同空间上
的不同方法。 -- 线程要记录的一项事务时目前线程执行空间做到哪里。
- 如何启动新的线程
注: 每个 Thread 需要一个任务来执行, 一个可以放在执行空间的任务
对Thread而言, 它是个工人,而 Runnable 就是这个工人的工作。( Runnable带有会放在执行空间的第一项方法 run() )
- 新建立线程的 3 个状态
通常线程会在可执行与执行中两种状态中来回交替,线程有可能会暂时被挡住,例如调用某个被锁住的对象的方法,此时
线程就的等到锁住对象的线程放开这个对象才能继续执行,这类型的条件会导致线程暂时失效。(由线程调度器控制)
- 线程调度器
线程调度器会决定哪个线程从等待状态中被挑出来运行,以及何时把哪个线程送回等待被执行的状态。你无法控制调度器
注: 一旦启动线程, 程序的顺序就不受控制,并不是线程执行完了才执行线程下边的语句,记住,它们是独立的执行空间
一个线程只能执行一项任务. 一旦开始执行,就不能再接其他任务。
- 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, 如果可以拿到钥匙才会进入方法.
注: 同步化方法越少越好, 而且范围越小越好, 只要考虑原子性问题的时候, 才考虑同步化
同步化一定要注意,会引起“死锁”
foo 的到了对象A的锁,同时想申请对象B的锁,同时 bar得到了对象B的锁,想申请对象A的锁,那么就只有等待。。。