1.15
回顾:
精华笔记:
技能点:线程、socket聊天室、服务端;
一、多线程并发安全
1. 相关概念:
a.线程: 单一顺序的控制流;
b.多线程:并发执行的单一顺序控制流;
c.并发:微观上走走停停,宏观上一起运行的现象;
2、线程
a.分类:
用户线程:
守护线程:普通线程调用setDaemon(true)方法得到的线程;
b.线程的两种创建方式;
*继承thread重写run方法: 优点:结构简单;
缺点:不能再继承其它类、不利于线程的重用;
* 实现Runnable接口 优点:a可以多实现,没有继承冲突; b. 便于线程的重用; 缺点:创建稍复杂;
c.线程信息的获取 ;
线程优先级:线程只能被动的被分配时间片并发运行,而线程调度器负责均 匀的将时间片分配给线程;
睡眠阻塞: static void sleep(long ms)
线程进入阻塞状态,超时后等待,再次获取时间片并发运行;
d.线程有关的方法:
启动: static Thread currentThread():
属性: long getId():
线程信息: void setPriority(int priority):
判断: boolean isDaemon():
3.多线程并发安全:并发运行时,由于切换时机的不一致,导致执行顺序出现的混乱;
synchronized关键字、
互斥锁; synchronized锁定多个代码段的同时,锁对象是同一个,那么这些代 码段是互斥的;
Foo foo=new Foo();
Class Coo{
public synchronized void methodA(){
}
public synchronized void methodB(){
}
}
Foo对象的两个方法,一个类只能同时对应一个对象,故互斥锁是同步有序的;
、、互斥和同步还是不同的。
二、synchronized关键字;
两种方式
同步方法:被synchronized修饰的方法;
同步块: a.定义:锁定排队的代码段;
b.语法: synchronized(同步监视器对象){
多线程同步执行的代码段 };
c.(同步监视器对象即上锁的对象):
------------修饰类、实例方法、静态方法、代码块;
锁对象为实例方法时,锁对象是this;当new 一个对象,一个类只对应一个对象,故此时是同步有顺序的;//而 new两个对象时,此时相互不影响this不同,可以同时进行;
实例方法
new一个对象,同步;
Shop shop=new Shop();
Class Coo{
public synchronized void buy(){
}
}
------------
new两个对象,同时
Shop shop1=new Shop();
Shop shop2=new Shop();
Class Coo{
public synchronized void buy(){
}
}
锁对象为静态时,代码段同步执行;
当synchronized修饰静态方法时,此时锁对象为类对象;当synchronized在同步块中 时,锁对象为类名.class;
含静态方法的同步方法
Class Boo{
public synchronized static void dosome(){
}
}
含静态方法的同步块
Class Boo{
public static void dosome(){
synchronized(Boo.class){
}
}
}
d.要求:同步监视器对象是同一个;
三、聊天室;
a. 目的:建立服务器端与客户端并进行连接,实现发消息等功能;
b.思路:
客户端:1、建立连接;2、第一次通讯 ;3、循环发消息;
4、多客户端链接(利用多线程)
服务端:5、服务端发消息; 6、服务器转发所有消息;
c.步骤(语句、方法、对象):
1.建立连接;
客户端: 建Client类,声明socket对象,写无参构造方法(实例化socket)、开始工作start方法(包含了getOutputStream方法);
服务端: 建Server类,声明serversocket对象,写无参构造方法(实例化8088端口、 getInputStream()方法)、开始工作start的方法(包含了socket accept阻塞方法);
客户端
public Class Client{
private Socket socket;
public Client(){
try{
System.out.println("正在链接服务端");
socket=new Socket("localhost",8088);
System.out.println("成功链接服务端");
} catch (IOException e){
e.printStackTrace();
}
}
public void start(){
OutputStream out=socket.getOutputStream();
OutputStreamWriter osw=new OutputStreamWriter(out);
bufferedwriter bw=new bufferedwriter(osw);
printWriter pw=new printWriter(bw);
}
public static void main(String[] args){
Client client=new Client();
client.start();
}
}
---------------------------------------------------------------------
服务端:
Class Server{
private ServerSocket serverSocket;
public Server(){
try{
System.out.println("正在启动服务端");
serverSocket=new ServerSocket(8088);
System.out.println("服务端启动完毕");
}catch(IOException e){
e.printStackTrace();
}
public void start(){
try{
System.out.println("链接客户端");
Socket socket=serverSocket.accept();
System.out.println("与一个客户端连接了");
}catch(IOException e){
e.printStackTrace();
}
public static void main(String[] args){
Server server=new Server();
server.start();
}
}
2、第一次通讯(发送一行字符串)
客户端:在start方法添加getOutputStream方法,扫描定义字符串、if分支结构判断并输出;通讯完毕,添加socket.close()方法;----try-catch捕获异常
服务端:在start方法添加getInputStream方法,定义空字符串并输出;通讯完毕,添加socket.close()方法;----try-catch捕获异常
客户端
public Class Client{
private Socket socket;
public Client(){
try{
System.out.println("正在链接服务端");
socket=new Socket("localhost",8088);
System.out.println("成功链接服务端");
} catch (IOException e){
e.printStackTrace();
}
}
public void start(){
2✌ try{ OutputStream out=socket.getOutputStream();
OutputStreamWriter osw=new OutputStreamWriter(out);
bufferedwriter bw=new bufferedwriter(osw);
printWriter pw=new printWriter(bw);
Scanner scanner=new Scanner(System.in);
String line=scanner.nextLine();
if("exit".equals(line)){
break;
}
pw.println(line);
System.out.println(line);
}
}catch(IOException e){
e.printStackTrace();
}finally{
try{
socket.close();
}catch(IOException e){
e.printStackTrace();
}
}
✌
}
public static void main(String[] args){
Client client=new Client();
client.start();
}
}
---------------------------------------------------------------------
服务端:
Class Server{
private ServerSocket serverSocket;
public Server(){
try{
System.out.println("正在启动服务端");
serverSocket=new ServerSocket(8088);
System.out.println("服务端启动完毕");
}catch(IOException e){
e.printStackTrace();
}
public void start(){
try{
System.out.println("链接客户端");
Socket socket=serverSocket.accept();
System.out.println("与一个客户端连接了");
}catch(IOException e){
e.printStackTrace();
客户端
public Class Client{
private Socket socket;
public Client(){
try{
System.out.println("正在链接服务端");
socket=new Socket("localhost",8088);
System.out.println("成功链接服务端");
} catch (IOException e){
e.printStackTrace();
}
}
public void start(){
OutputStream out=socket.getOutputStream();
OutputStreamWriter osw=new OutputStreamWriter(out);
bufferedwriter bw=new bufferedwriter(osw);
printWriter pw=new printWriter(bw);
}
public static void main(String[] args){
Client client=new Client();
client.start();
}
}
---------------------------------------------------------------------
服务端:
Class Server{
private ServerSocket serverSocket;
public Server(){
try{
System.out.println("正在启动服务端");
serverSocket=new ServerSocket(8088);
System.out.println("服务端启动完毕");
}catch(IOException e){
e.printStackTrace();
}
public void start(){
try{
System.out.println("链接客户端");
Socket socket=serverSocket.accept();
System.out.println("与一个客户端连接了");
}catch(IOException e){
e.printStackTrace();}
try{2👏InputStrem in=socket.getInputStream();
InputStreamReader isr=new InputStreamReader(in);
bufferedReader br=new bufferedReaader(isr);
String line;
System.out.println("客户端:"+line);
}catch(IOException e){
e.printStackTrace();
}finally{
try{
socket.close();
}catch(IOException e){
e.printStackTrace();
}👏
};
public static void main(String[] args){
Server server=new Server();
server.start();
}
}
3、实现循环发消息
客户端:设置while(true)死循环;
服务端:设置printWriter为null;添加br的readLine()方法不等于空时的while循环并输 出;
4、 多线程实现多客户端的链接;
客户端: 读取服务端发送消息的线程并启动,
a. start方法(new serverHandler对象 ,实例化线程设置守护线程并启动);
b. 添加ServerHandler类,实现Runnable重写run方法;
c.run方法:获取getInputStream方法,添加读取br.readLine()!=null的循环并输出;
5、服务端发送消息给客户端;
服务端:
a、start方法中,new一个客户端处理程序clientHandler对象、实例化thread并启动;
b、添加ClientHandler类实现Runnable重写run方法,在run方法加入getInputStream()方
法和getOutputStream方法。
c、在clientHandler类中,定义成员变量socker/host,添加clientHandler的有参构造方法;
6、服务器转发所有消息给客户端;
a. 定义一个list类型的allOut集合;
b.在clientHandler类中添加sendMessage有参方法,发送消息并用增强for循环遍 历,加入同步块;
c.广播上线消息: run方法中,将加入输出流pw的allOut的add方法中,设置同步块;
广播下线信息:在客户端发送消息后面加入pw的allOut 的remove方法,并设置同步块;
d.代码:
四、服务端操作流程;
关键在于new文件与处理流两处的不同;
服务器处理流程代码思路笔记
UserController类---类注解;
保存所有用户信息目录/static静态块;
一, reg方法--------注解@RequestMapping;
1.解析请求;
a. 生成对象HttpservletRequest.HttpServletResponse;
b. getparameter获取表单;
2. 处理请求;------ 数据验证
3. 发送响应;------ sendRedirect方法重定向;
a.若为空,,, new user对象//重载file构造器;
b.验证存在性,
若存在,重定向have_user.html;
c. 流处理,writeObject()方法保存文件; 注册成功响应重定向;
-----------------------------------------------------------------------------------------------------------------------
二、 login方法
1.请求对象;----获取表单
2.处理请求;
a. 数据验证;
b.若为null,根据用户名去users目录下定位, new file;
c. 验证存在性,
反序列化该文件;把readObject()方法进行强制转换为user,
看当前密码与注册密码是否一致;
3.发送响应;
若一致,则发送响应重定向,login_success.html;
若失败,响应重定向为login_fail.html;
-----------------------------------------------------------------------------
三、 userList方法---------注解@RequestMapping;
请求对象;
1. 准备在页面上展示的数据
读取目录信息;----------------list集;
获取目录下的.obj文件;--file数组;
处理请求;
2. 存入userList集合备用:将obj文件的user对象反序列化,强转,存入userList集合;
发送响应;
3、 向浏览器发送html代码;(两个常用方法;)
setContentType()方法 : 设置发送到客户端的响应的内容类型;
getWriter()方法: 返回Servlet引擎创建的字符输出流对象
笔记: