8.多线程和Socket通信

时间:2022-03-16 17:56:39

8.多线程和Socket通信

一、多线程

1、进程的概念:

进程就是应用程序的执行实例,有独立的内存空间和系统资源。当一个应用程序没有执行的时候,它就不是一个进程。

 

2、进行的特征:

(1)动态性:动态产生动态消亡。

(2)并发性:多个应用程序同时在运行。

(3)独立性:独立运行。

 

3、线程的概念:

线程是进行内部的一个执行单元,它是程序中一个单一的顺序控制流程。

 

4、线程的特点:

(1)一个进程可以包含多个线程,而一个线程至少要有一个父进程。

注意:一个线程是不能离开进程独立存在的

(2)线程可以有自己的堆栈,程序计数器和局部变量。

(3)线程与父进程的其他线程共享进程中所有的资源。

(4)独立运行,采用独占方式。

(5)一个线程可以创建和删除另外一个线程。

(6)同一个进程中的多个线程之间可以并发执行。

(7)线程的调度管理是由进行来完成的

 

5、每个程序至少自动拥有一个线程,称为主线程。当程序加载到内存中,启动主线程。

 

6、Java中的public static void main()方法是主线程的入口。

 

7、使用线程的四个步骤:

(1)定义线程,并执行该线程完成的功能。

(2)创建线程对象

(3)启动线程

(4)终止线程

 

8、创建线程的两种方式:

(1)继承Thread类创建线程

  特点:要重写父类的run()方法,该方法是线程要执行操作任务的方法。线程操作的代码都放入run方法中,然后调用start()方法启动线程

案例1.继承Thread类创建线程

 package com.part1;
/**
*1.继承Thread类创建线程,==当前类就是一个线程类
*特点:编写简单,可以直接操作线程,适用于单继承的情况
*操作:使用线程计算打印1-100之间的值
*
*/
public class MyThread extends Thread{
//1.创建记录数的变量
private int count=0;
//2.创建线程执行任务的方法run()
@Override
public void run() {
while(count<100){
count++;
System.out.println("count的值是:"+count);
}
}
//3.编写主线程main()
public static void main(String[] args) {
//3.1 实例化线程对象,==线程已经存在
MyThread mt=new MyThread();
//3.2 使用start启动线程对象=执行run方法内容
mt.start();
}
}

MyThread.java

(2)继承自Runnable接口创建线程

特点:可以使多个线程共用一个Runnable对象,实现资源共享

案例2.实现Runnable接口创建线程

 package com.part1;
/**
* 2.实现Runnable接口创建线程
* 特点:
* 当一个线程继承了另一个类,就只能用实现Runnable接口就的方法来创建线程
* 这种方法可以使多个线程之间公用一个Thread对象
* @author pc
*
*/
public class MyRunnable implements Runnable {
//1.定义统计的变量
private int count=0;
//2.定义线程执行的任务方法
@Override
public void run() {
while(count<100){
count++;
System.out.println("count的值是:"+count);
}
}
//3.定义主线程入口
public static void main(String[] args) {
//3.1 创建线程对象
Thread thread=new Thread(new MyRunnable());
//3.2 启动线程
thread.start(); } }

MyRunnable.java

9、线程的状态及线程的生命周期:新生状态、可运行状态、阻塞状态、死亡状态。

(1)新生状态:创建线程对象之后,但是未调用start之前的状态。

(2)可运行状态:调用start()方法启动线程之后的状态

(3)阻塞状态:一种暂时不可运行状态。有可能还会转回可运行状态

(4)死亡状态:当run方法运行完毕后的状态。

10、线程优先级:1-10,1表示最高,可以通过setPriority(int  grade)方法更改,

11、Thread类常用的方法:

(1)void run()  :执行线程任务操作的方法

(2) void start():  使线程开始执行的方法

(3) void join() : 等待当前该线程终止,等待join上面的线程执行完再接着刚的执行

案例3.使用join方法暂停执行当前线程

 package com.part1;
/**
* 3.使用join方法暂停执行当前线程,等待调用该方法的线程结束后再继续执行本线程
* 操作:在主现场中调用另个一个线程,遇到在调用某个现场后,遇到join时,将某个
* 线程所有操作完成才能接着执行主线程内容
* @author Holly
*
*/
public class MyJoin extends Thread{
//1.定义有参数构造,获取本线程的名字
public MyJoin(String name){
super(name);
} //2.定义线程执行任务的方法
public void run(){
//2.1 循环输出5次该线程对象的名字
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
//3.定义主线程入口
public static void main(String[] args) {
//3.1 主线程循环10次,在第五次时调用该类线程对象,并执行join方法,
for (int i = 0; i < 10; i++) {
if(i==5){
try {
MyJoin mj=new MyJoin("MyJoin");
mj.start();
mj.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" "+i); }
} }

MyJoin.java

案例4.使用join方法实现两个线程间数据传递

 package com.part1;
/**
* 4.使用join方法实现两个线程间数据传递
*/
public class MyJoinSetField extends Thread{
//1 定义两个变量
public String value1;
public String value2; //2.定义线程执行操作的方法
public void run(){
value1="value1已经赋值";
value2="value2已经赋值"; }
//3.定义主线程入口
public static void main(String[] args) {
try{
//创建线程对象
MyJoinSetField mjsf=new MyJoinSetField();
//启动线程
mjsf.start();
mjsf.join();
System.out.println("value1:"+mjsf.value1);
System.out.println("value2:"+mjsf.value2);
} catch (InterruptedException e) {
e.printStackTrace();
}
} } MyJoinSetField.java

MyJoinSetField.java

(4) String getNam() :返回该线程的名称

(5)void sleep(long 毫秒) :让正在执行的线程休眠,暂停执行,进入阻塞状态

案例5.sleep使得线程处于休眠状态

 package com.part1;
/**
* 5.sleep使得线程处于休眠状态,不可运行状态,也就是阻塞状态,
* 但是一定时候又会执行
* @author pc
*
*/
public class SleepWait {
//1.定义方法使得当前线程自我休眠5秒
public static void bySecond(long s){
try {
//每次睡眠一秒,睡五次
for (int i = 0; i < s; i++) {
System.out.println(i+1+"秒");
//线程自我睡眠1秒
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//2.主线程入口
public static void main(String[] args) {
//2.1提示等待
System.out.println("wait");
//2.2 让主线程等待5秒后再次执行
SleepWait.bySecond(5);
//2.3 提示恢复执行
System.out.println("start"); } }

SleepWait.java

(6)void yield() :暂停当前正在执行的线程对象,执行其他线程,此时处于等待可运行状态

案例6.yield方法暂停线程

6.1 第一个线程类

 package com.part1;
/**
* 6.yield方法暂停线程,让优先级比自己高的先执行,
* 但是该现场时处于可运行状态,而不是阻塞状态
* @author pc
*
*/
public class YieldFirstThread extends Thread{
/**
* 线程任务执行的方法
*/
@Override
public void run() {
for (int i = 1; i <=5; i++) {
System.out.println("第一个线程,第"+i+"次运行!");
Thread.yield();
}
} }

YieldFirstThread.java

6.2 第二个线程类

 package com.part1;
/**
* 6.yield方法暂停线程,让优先级比自己高的先执行,
* 但是该现场时处于可运行状态,而不是阻塞状态
* @author pc
*
*/
public class YieldSecondThread extends Thread{
/**
* 线程任务执行的方法
*/
@Override
public void run() {
for (int i = 1; i <=5; i++) {
System.out.println("第二个线程,第"+i+"次运行!");
Thread.yield();
}
} }

YieldSecondThread.java

6.3 yield方法的测试类

 package com.part1;
/**
* 6.yield方法的测试类
* @author pc
*
*/
public class YieldTest {
/**
*
* @param args
*/
public static void main(String[] args) {
YieldFirstThread firstThread=new YieldFirstThread();
YieldSecondThread secondThread =new YieldSecondThread();
firstThread.start();
secondThread.start();
} }

YieldTest.java

案例7:多线程通信

7.1 储蓄罐ZhuBaoBao

 package com.part5;
/**
* 1.储蓄罐
* 资源对象
* @author pc
*
*/
public class ZhuBaoBao {
/**
* 1.储蓄罐余额
*/
private int money; public ZhuBaoBao() {
} public ZhuBaoBao(int money) {
this.money = money;
}
/**
* 2.存钱
* @param money
*/
public void add(int inmoney){
try {
//同步代码块
synchronized (this) {
//储蓄罐现有余额
int oldmoney=this.money;
//让当前线程自我休眠40毫秒,
//然后使得其他线程有机会访问该资源对象
Thread.sleep(40);
//存钱
money=oldmoney+inmoney;
System.out.println("宝哥哥存入"+inmoney+"元,现有余额"+money+"元。"); //通知等待的线程开启获取资源
notifyAll();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 3.取钱
* 线程同步方法
* @param outmoney
*/
public synchronized void get(int outmoney){
try {
if(outmoney>money){
System.out.println("余额"+money+"林妹妹等待...");
//线程等待
wait();
System.out.println("等待结束,再判断");
}
//储蓄罐现有余额
int oldmoney=money; //让当前线程自我休眠30毫秒,让其他线程有机会访问该资源对象
Thread.sleep(30);
//取钱
money=oldmoney-outmoney;
System.out.println("林妹妹取走"+outmoney+"元,先有余额"+money+"元。"); } catch (InterruptedException e) {
e.printStackTrace();
}
} public int getMoney() {
return money;
} public void setMoney(int money) {
this.money = money;
} @Override
public String toString() {
return "我是猪宝宝,我体内现有余额是:" + money + "元。";
} }

ZhuBaoBao.java

7.2 宝哥哥存钱类

 package com.part5;
/**
* 2.宝哥哥存钱类,操作猪宝宝储蓄罐
* @author pc
*
*/
public class BaoGeGe implements Runnable {
//1.引入储蓄罐操作对象
private ZhuBaoBao zhuBaoBao; public BaoGeGe() {
} public BaoGeGe(ZhuBaoBao zhuBaoBao) {
this.zhuBaoBao = zhuBaoBao;
} /**
* 2.线程操作执行方法
*/
@Override
public void run() {
//每次存100 一共存10次
for (int i = 1; i <=10; i++) {
System.out.println("宝哥哥第"+i+"次存钱");
//存钱
zhuBaoBao.add(100);
}
} public ZhuBaoBao getZhuBaoBao() {
return zhuBaoBao;
} public void setZhuBaoBao(ZhuBaoBao zhuBaoBao) {
this.zhuBaoBao = zhuBaoBao;
} }

BaoGeGe..java

7. 3 林妹妹取钱类

 package com.part5;
/**
* 3.林妹妹取钱类
* @author pc
*
*/
public class LinMeiMei implements Runnable {
//1.引入操作对象
private ZhuBaoBao zhuBaoBao; public LinMeiMei() {
} public LinMeiMei(ZhuBaoBao zhuBaoBao) {
this.zhuBaoBao = zhuBaoBao;
} /**
* 2.线程任务操作方法
*/
@Override
public void run() {
get(); //取钱 }
/**
* 3.林妹妹取钱
*/
private void get() {
for (int i = 1; i <=10; i++) {
System.out.println("林妹妹第"+i+"次取钱");
zhuBaoBao.get(200);
} } public ZhuBaoBao getZhuBaoBao() {
return zhuBaoBao;
} public void setZhuBaoBao(ZhuBaoBao zhuBaoBao) {
this.zhuBaoBao = zhuBaoBao;
} }

LinMeiMei.java

7.4 测试类

 package com.part5.synchronizedlock;
/**
* 4.测试类
*/
public class Test {
public static void main(String[] args) {
try {
//1.获取当前线程
Thread mainThread=Thread.currentThread(); //2.初始化猪宝宝储蓄罐余额1000元
ZhuBaoBao zhubaobao=new ZhuBaoBao(1000); //3.线程宝哥哥和林妹妹操作的是同一个对象猪宝宝
BaoGeGe baoGeGe=new BaoGeGe(zhubaobao);
LinMeiMei linMeiMei=new LinMeiMei(zhubaobao); //4.创建线程类
Thread t1=new Thread(baoGeGe);
Thread t2=new Thread(linMeiMei); //5.启动线程
t1.start();
t2.start(); //6.主线程自我休眠3秒以后以便其他两个线程先执行操作
mainThread.sleep(3000); //7.打印账户余额
System.out.println(zhubaobao); } catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} } }

Test.java

7.5 结果

林妹妹第1次取钱
宝哥哥第1次存钱
林妹妹取走200元,先有余额800元。
林妹妹第2次取钱
林妹妹取走200元,先有余额600元。
林妹妹第3次取钱
宝哥哥存入100元,现有余额700元。
宝哥哥第2次存钱
宝哥哥存入100元,现有余额800元。
宝哥哥第3次存钱
宝哥哥存入100元,现有余额900元。
宝哥哥第4次存钱
宝哥哥存入100元,现有余额1000元。
宝哥哥第5次存钱
林妹妹取走200元,先有余额800元。
林妹妹第4次取钱
林妹妹取走200元,先有余额600元。
林妹妹第5次取钱
林妹妹取走200元,先有余额400元。
林妹妹第6次取钱
林妹妹取走200元,先有余额200元。
林妹妹第7次取钱
林妹妹取走200元,先有余额0元。
林妹妹第8次取钱
余额0林妹妹等待...
宝哥哥存入100元,现有余额100元。
宝哥哥第6次存钱
宝哥哥存入100元,现有余额200元。
宝哥哥第7次存钱
宝哥哥存入100元,现有余额300元。
宝哥哥第8次存钱
宝哥哥存入100元,现有余额400元。
宝哥哥第9次存钱
宝哥哥存入100元,现有余额500元。
宝哥哥第10次存钱
宝哥哥存入100元,现有余额600元。
等待结束,再判断
林妹妹取走200元,先有余额400元。
林妹妹第9次取钱
林妹妹取走200元,先有余额200元。
林妹妹第10次取钱
林妹妹取走200元,先有余额0元。

二、Socket通信

2.1 案例1:客户端写入,服务器短读取

 package com.part1;

 import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException; /**
* 1.客户端写入
* @author pc
*
*/
public class SocketTest {
public static void main(String[] args) throws UnknownHostException, IOException {
//1.建立客户端的连接,指定服务器的地址和端口号
Socket socket=new Socket("localhost", 8080);
//2.创建输出流对象
OutputStream os=socket.getOutputStream();
//3.向服务器段写入内容
String info="用户名:Holly,密码:123";
os.write(info.getBytes()); //4.关闭资源
os.close();
socket.close();
} }

SocketTest .java

 package com.part1;

 import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket; /**
* 2.服务器段 读取内容
* @author pc
*
*/
public class ServiceSocketTest {
public static void main(String[] args) throws IOException {
//1.创建一个服务器对象并指定监听的端口号
ServerSocket serverSocket=new ServerSocket(8080);
//2.等待客户端触发通信
Socket socket=serverSocket.accept();
//3.创建输入流对象
InputStream is=socket.getInputStream();
//字节流转换为字符流
BufferedReader br=new BufferedReader(new InputStreamReader(is));
//4.循环读取某行内容
String str=br.readLine();
while(str!=null){
System.out.println("我是服务器,客户端信息为:"+str);
str=br.readLine();
}
//5.关闭流
br.close();
is.close();
socket.close();
serverSocket.close();
} }

ServiceSocketTest.java

2.2 案例:2:服务器写入,客户端读取

 package com.part2;

 import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket; /**
* 1.服务器段写入
* @author pc
*
*/
public class ServiceSocketTest {
public static void main(String[] args) throws IOException {
//1.创建一个服务器对象,并指定监听端口号
ServerSocket serverSocket=new ServerSocket(8080);
//2.等待客户端触发通信
Socket socket=serverSocket.accept();
//3.创建输出流对象
OutputStream os=socket.getOutputStream();
//4.向客户端写入内容
String str="你今天萌萌哒!";
os.write(str.getBytes());
//5.释放资源
os.close();
socket.close();
serverSocket.close(); } }

ServiceSocketTest.java

 package com.part2;

 import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket; /**
* 2.客户端读取信息
* @author pc
*
*/
public class SocketTest {
public static void main(String[] args) throws IOException {
//1.建立客户端的连接,指定服务器的地址和端口号
Socket socket=new Socket("localhost", 8080);
//2.创建输入流,读取服务器信息
InputStream is=socket.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(is)); //3.循环读取某行内容
String str=br.readLine();
while(str!=null){
System.out.println("我是客户端,服务器的相应为:"+str);
str=br.readLine();
} //4.关闭流
br.close();
is.close();
socket.close(); } }

SocketTest.java

2.3 案例3:客户登录

1. 创建用户类

 package com.part3;

 import java.io.Serializable;
/**
* 1.创建用户类
* @author pc
*
*/
public class User implements Serializable {
private static final long serialVersionUID = -7668755635308104712L;
private String name;
private transient String pwd; public User() {
}
public User(String name, String pwd) {
this.name = name;
this.pwd = pwd;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
@Override
public String toString() {
return "User [name=" + name + ", pwd=" + pwd + "]";
} }

User.java

2. 登录客户端

 package com.part3;

 import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket; /**
* 2.客户端写入
* 序列化
* @author holly
*
*/
public class LoginClient {
public static void main(String[] args) throws IOException {
//1.建立客户端连接,指定服务器的地址和端口号
Socket socket=new Socket("localhost", 8080);
//2.创建输出流对象
OutputStream os=socket.getOutputStream();
//3.创建序列化对象
ObjectOutputStream oos=new ObjectOutputStream(os);
//4.发送客户端登陆信息=向输出流写入信息,=向服务器写入
User user=new User("holly", "123");
oos.writeObject(user);
//5.释放资源
oos.close();
os.close();
socket.close(); } }

LoginClient.java

3.登录服务器

 package com.part3;

 import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket; /**
* 3.服务器读取
* 反序列化操作
* @author pc
*
*/
public class LoginServer {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//1.创建服务器对象,并指定监听的端口号
ServerSocket serverSocket=new ServerSocket(8080);
//2.等待客户端触发通信
Socket socket=serverSocket.accept();
//3.创建输入流对象
InputStream is=socket.getInputStream();
//4.创建反序列对象
ObjectInputStream ois=new ObjectInputStream(is);
//5.读取客户端信息
User user=(User) ois.readObject();
if(user!=null){
System.out.println("我是服务器,客户端信息为:"+user);
}
//6.关闭流
ois.close();
is.close();
socket.close();
serverSocket.close();
} }

LoginServer.java

2.4 案例4:多个客户登录-线程

1.创建用户类

 package com.part4;

 import java.io.Serializable;
/**
* 1.创建用户类
* @author pc
*
*/
public class User implements Serializable {
private static final long serialVersionUID = -7668755635308104712L;
private String name;
private transient String pwd; public User() {
}
public User(String name, String pwd) {
this.name = name;
this.pwd = pwd;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
@Override
public String toString() {
return "User [name=" + name + ", pwd=" + pwd + "]";
} }

User.java

2.创建登录客户端1

 package com.part4;

 import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket; /**
* 1.客户端写入
* 序列化
* @author Holly
*
*/
public class LoginClient1 {
public static void main(String[] args) throws IOException {
//1.建立客户端连接,指定服务器地址和端口号
Socket socket=new Socket("localhost",8888);
//2.创建输出流对象
OutputStream os=socket.getOutputStream();
//3.创建序列化对象
ObjectOutputStream oos=new ObjectOutputStream(os);
//4.发送客户端信息到服务器,=向服务器写入信息
User user=new User("holly", "123");
oos.writeObject(user);
//5.释放资源
oos.close();
os.close();
socket.close(); } }

LoginClient1.java

3.创建登录客户端2

 package com.part4;

 import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket; /**
* 2.客户端写入
* @author pc
*
*/
public class LoginClinet2 {
public static void main(String[] args) throws IOException {
//1.建立客户端连接,指定服务器地址和端口号
Socket socket=new Socket("localhost", 8888);
//2.创建输出流对象
OutputStream os=socket.getOutputStream();
//3.创建序列化对象
ObjectOutputStream oos=new ObjectOutputStream(os);
//4.发送客户端登陆信息,==向输出流写入内容
User user=new User("张福军", "123");
oos.writeObject(user); //5.创建输入流对象
InputStream is=socket.getInputStream();
//6.接受服务器段的响应,即从输入流读取信息
BufferedReader br=new BufferedReader(new InputStreamReader(is)); String str=br.readLine();
while(str!=null){
System.out.println("我是客户端,服务器的响应为:"+str);
}
//7.关闭资源
br.close();
is.close();
oos.close();
os.close();
socket.close(); } }

LoginClinet2.java

4.登陆线程类(客户端)

 package com.part4;

 import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.Socket; /**
* 3.登陆线程类(客户端)
* @author pc
*
*/
public class LoginThread extends Thread{
//1.定义Socket对象
Socket socket=null; //2.每启动一个线程,对应分配一个Socket
public LoginThread(Socket socket) {
this.socket = socket;
}
//3.线程操作执行的方法=启动线程,=响应客户端信息=读取
@Override
public void run() {
try {
//3.1创建输入流对象
InputStream is=socket.getInputStream();
//3.2 创建反序列化对象
ObjectInputStream ois=new ObjectInputStream(is);
//3.3 获取客户端信息=从输入流读取信息
User user=(User) ois.readObject();
if(user!=null){
System.out.println("我是服务器,客户端信息为:"+user);
}
//3.4关闭资源
ois.close();
is.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }

LoginThread.java

5.创建登录服务器

 package com.part4;

 import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket; /**
* 4.登录服务器
* @author pc
*
*/
public class LoginServer {
public static void main(String[] args) throws IOException {
//1.建立服务器对象,并指定监听端口号
ServerSocket serverSocket=new ServerSocket(8888);
//2.创建Socket客户端连接对象
Socket socket=null;
//3.循环监听一只进行中
while(true){
//3.1 等待客户端触发通信
socket=serverSocket.accept();
//3.2 创建线程对象,来几个客户端创建几个线程,建立几个通信
LoginThread loginThread=new LoginThread(socket);
//3.3 启动线程
loginThread.start();
} } }

LoginServer.java