1 Socket连接
1.连接
2.传送
3.接收
Socket chatSocket = new Socket("196.164.1.103", 5000);
端口号有16位宽,范围为0-65535,其中0-1023都被保留给已知的特定服务。
网页服务器是80。Telnet服务器是23。POP3邮件服务器是110。SMTP邮局交换服务是25。
2 读取Socket
127.0.0.1这个IP地址代表本机,所以可以在本机同时测试客户端和服务器。
//1.建立对服务器的Socket连接
Socket chatSocket = new Socket("127.0.0.1",5000);
//2.建立连接到Socket低层输入串流的InputStreamReader,InputStreamReader转换字节为字符
InputStreamReader stream = new InputStreamReader(chatSocket.getInputStream());
//3.建立BufferedReader来读取
BufferedReader reader = new BufferedReader(stream);
String message = reader.readLine();
3 写入Socket
Socket chatSocket = new Socket("127.0.0.1",5000);
PrintWriter stream = new PrintWriter(chatSocket.getOutputStream());
writer.println("message to send");
writer.print("another message");
4 客户端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.logging.SocketHandler;
public class DailyAdviceClient {
public static void main(String[] args) {
DailyAdviceClient client = new DailyAdviceClient();
client.go();
}//end main
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();
}
}//end go
}
5 服务器
服务器需要一对Socket,一个是会等待用户请求的ServerSocket,一个是与用户通信用的Socket。
1.服务器应用程序对特定端口创建出ServerSocket。
ServerSocket serverSock = new ServerSocket(4242);
2.服务器端创建出与客户端通信的Socket
Socket sock = serverSock.accept();
accept()方法会在等待用户的Socket连接时闲置。当用户连上来时,此方法会返回一个Socket(在不同的端口上),以便与客户端通信。Socket与ServerSocket的端口不同,因此ServerSocket可以空出来等待其他用户。
6 服务器端
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class DailyAdviceServer {
String [] adviceList = {"Take small bites", "Go for the tight jeans", "Happy", "Sad", "Cry"};
public void go() {
try {
System.out.println("begin");
ServerSocket serverSocket = new ServerSocket(4242);
while(true) {
//该方法会停下来等待要求到达后才会继续
Socket socket = serverSocket.accept();
PrintWriter writer = new PrintWriter(socket.getOutputStream());
String advice = getAdvice();
writer.println(advice);
writer.close();
}//end while
} catch (IOException ex) {
ex.printStackTrace();
}
}//end go
private String getAdvice() {
int random = (int) (Math.random() * adviceList.length);
return adviceList[random];
}
public static void main(String[] args) {
DailyAdviceServer server = new DailyAdviceServer();
server.go();
}
}
7 线程
1.建立Runnable对象(线程的任务)
Runnable threadJob = new MyRunnable();
2.建立Thread对象(执行工人)并赋值Runnable(任务)
Thread myThread = new Thread(threadJob)
3.启动Thread
myThread.start()
每个Thread需要一个任务来执行。该任务即
public void run() {
}
不同线程可以命名
(通常用来除错)
Thread alpha = new Thread(runner);
alpha.setName("Alpha thread");
String threadName = Thread.currentThread().getName();
8 线程调度器
无法控制调度,没有API可以调用调度器。
9 Runnable接口
接口可以用来声明子类
public class MyRunnable implements Runnable {
public void run() {
go();
}//end run
public void go() {
doMore();
}
public void doMore() {
System.out.println("Hello");
}
}
public class ThreadTester {
public static void main(String[] args) {
MyRunnable threadJob = new MyRunnable();
Thread myThread = new Thread(threadJob);
myThread.start();
System.out.println("main");
}
}
可以不使用Runnable接口,用Thread的子类覆盖run()这个方法,然后调用Thread的无参数构造函数来创建出新的线程。
一旦线程的run()方法完成之后,该线程就不能再重新启动。
10 sleep()
确保其他线程能够有机会执行的最好方式是让它们周期性地休眠。
Thread.sleep(2000);
需要包在try/catch块中。
11 并发
多线程会引发并发性,并发性会引发竞争状态,竞争状态会引发数据的损毁。
两个或以上的线程存取单一对象的数据,即两个线程都对同一个对象执行getter或setter。
12 锁
每个对象都有一把锁,大部分时间都没有锁上,对象的锁只会在有同步化的方法上起作用。
如果对象有两个同步化的方法,就表示两个线程无法进入同一个方法,也表示两个线程无法进入不同的方法。
private synchronized void makeWithDraw(int amount) {
}
同步化会带来额外的成本。
查询钥匙等性能的损耗。
强制线程排队使进程变慢。
可能会导致死锁现象。
同步化可以只修饰部分指令,不必整个方法都同步化。
public void go() {
doStuff();
synchronized(this) {
criticalStuff();
morecriticalStuff();
}
}
13 死锁
数据库有处理死锁的机制,Java没有。
只要两个线程和两个对象就可以引起死锁。