多线程、网络编程 一、多线程 二、网络编程
一、多线程
1、并发和并行
2、进程的概念
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动。
3、线程的概念
4、线程调度
分时调度
所有的线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。
抢占式调度
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
5、创建多线程的第一种方式(Thread类)
java.lang.Thread 类
创建多线程必须要用到的类
步骤:
1、创建一个类继承Thread类
2、在这个类中重写thread中的run方法,来开启线程任务
3、在测试类中 创建thread的子类对象对象,调用它的start()方法来启动多线程的任务,来执行他的run方法
start() 方法 来开启线程 ;java虚拟机就会调用run方法
eg:
1、创建一个类来继承Thread类
public class MyThread extends Thread{
//重写Thread中的run方法
@override
public void run(){
//设置多线程的任务
}
}
2、主线程
public class Demo01Thread {
public static void main(String[] args) {
//3.创建Thread类的子类对象
MyThread mt = new MyThread();
//4.调用Thread类中的start方法,开启一个新的线程,执行run方法
mt.start();
//主线程会继续执行main方法中的代码
for (int i = 0; i < 20; i++) {
System.out.println("main-->"+i);
}
}
}
6、获取线程的名称
1、使用Thread类中的getName() 方法来获取
2、使用链式编程 Thread.currentThread.getName();
7、创建多线程的第二种方式(Runnable接口)
创建多线程的第二种方法 实现Runnable接口
java.lang.Runnable接口:
实现步骤:
1、创建一个类实现Runnable接口
2、该类重写Runnable中的run() 方法,设置多线程任务
3、创建Runnable的实现类对象
4、创建Thread类对象,构造方法中传入上面创建的Runnable对象作为参数
5、调用其start()方法 来开启多线程的任务 执行run方法
1、创建一个类实现Runnable接口
public class RunnableImpl implements Runnable{
//重写Runnable中的run方法
@override
public void run(){
。。。。。。设置多线程任务
}
}
3、创建Runnable实现类对象
RunnableImpl run = new RunnableImpl();
Thread t = new Thread(run);
t.start();
8、实现Runnable接口方式和继承Thread类方式的区别
1、单继承的局限性
使用Thread类来事项多线程,存在单继承的局限性,一个类只能继承一个类;继承Thread类,就不能继承其他的类
使用实现Runnable接口的方法:可以继承其他的类
2、实现Runnable接口把设置线程任务和开启线程继续了解耦,增强了扩展性
实现Runnable接口,目的就是重写run方法设置线程任务
创建Thread对象,目的传递Runnable接口的实现类对象(线程任务),执行线程任务
9、匿名内部类实现多线程
匿名内部类:
匿名:没有名字
内部类:写在其他类内部的类(成员,局部)
作用:简化代码
把子类继承父类,重写父类中的方法,创建子类对象合成一步完成
把实现类实现接口,重写接口中的方法,创建实现类对象合成一步完成
格式:
new 父类/接口(){
重写父类/接口中的方法;
}
注意:
匿名内部类最终的产生就是一个子类/实现类对象
//父类:Thread
//new MyThread().start();
new Thread(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}.start();
//接口:Runnable
//多态:父类的引用指向了子类的对象
// Runable r = new RunnableImpl();
//new Thread(r).start();
//new Thread(new RunnableImpl()).start();
Runnable r = new Runnable(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
new Thread(r).start();
new Thread(new Runnable(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}).start();
}
}
10、线程安全问题
线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
11、出现安全问题的原因
package com.itheima.demo10payTicket;
/*
卖票案例
*/
public class RunnableImpl implements Runnable{
//定义共享的票源
private int ticket = 100;
//线程任务:卖票
@Override
public void run() {
//让卖票重复执行
while (true){
//票大于0,就进行卖票
if(ticket>0){
//让线程睡眠10毫秒,提供安全问题出现的几率
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票!");
ticket--;
}
}
}
}
----------------------------------------------------------------------------
package com.itheima.demo10payTicket;
/*
创建3个线程进行买相同的100张票
*/
public class Demo01PayTticket {
public static void main(String[] args) {
RunnableImpl r = new RunnableImpl();
Thread t0 = new Thread(r);
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t0.start();
t1.start();
t2.start();
}
}
12、解决安全问题的方式一(同步代码块)
解决线程安全问题的第一种方式:使用同步代码块
格式:
synchronized(锁对象){
访问了共享数据的代码(产生线程安全问题的代码)
}
注意:
锁对象可以是任意的对象new Person new Object new Studnet
必须保证所有的线程使用的是同一个锁对象(唯一)
13、同步的原理以及解决安全问题的方式二(同步方法)
实现步骤:
把产生线程安全问题的代码提取出来,放到一个方法中
给方法添加一个同步(synchronized)的修饰符
静态的同步方法
锁对象是谁?
是this吗,不是,静态方法优先于this加载到内存中
是本类的class文件对象:RunnableImpl.class(反射)
14、解决安全问题的方式三(Lock锁)
解决线程安全问题的第三方式:使用Lock锁(了解)
java.util.concurrent.locks.Lock接口
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
接口中的方法:
void lock() 获取锁。
void unlock() 释放锁。
java.util.concurrent.locks.ReentrantLock implements Lock接口
实现步骤:
1.在成员位置创建Lock接口的实现类对象
2.在可能出现线程安全问题的代码前,使用lock方法获取锁对象
3.在可能出现线程安全问题的代码后,使用unlock方法释放锁对象
//定义共享的票源
private int ticket = 100;
//1.在成员位置创建Lock接口的实现类对象
private ReentrantLock rl = new ReentrantLock();
//线程任务:卖票
@Override
public void run() {
//让卖票重复执行
while (true){
//2.在可能出现线程安全问题的代码前,使用lock方法获取锁对象
rl.lock();
//票大于0,就进行卖票
if(ticket>0){
try {
//让线程睡眠10毫秒,提供安全问题出现的几率
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票!");
ticket--;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//3.在可能出现线程安全问题的代码后,使用unlock方法释放锁对象
rl.unlock();
}
}
}
}
二、网络编程
1、网络通信协议
udp:面向无连接的协议,通信双方不用连接连接,可以直接发送数据
好处:效率高
弊端:容易丢失数据
tcp:面向连接的协议,客户端和服务器必须经过3次握手连接逻辑连接,才能通信
好处:安全
弊端:效率低
2、端口号
3、TCP通信的原理
4、TCP通信的实现
创建TCP通信的客户端
/*
创建TCP通信的客户端:
向服务器发送连接请求和服务器经过3次握手连接逻辑连接;给服务器发送数据,接收服务器回写的数据
表示客户端的类:
java.net.Socket:此类实现客户端套接字(也可以就叫“套接字”)。
套接字:封装了IP地址和端口号的网络单位
构造方法:
Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。
参数:
String host:服务器的IP地址
int port:服务器的端口号
成员方法:
OutputStream getOutputStream()返回此套接字的输出流。
InputStream getInputStream() 返回此套接字的输入流。
注意:
客户端和服务器之间进行数据交互,不能使用自己创建的IO流对象,必须使用Socket中提供的网络流
实现步骤:
1.创建客户端Socket对象,封装服务器的IP地址和端口号
2.使用Socket对象中的方法getOutputStream获取网络字节输出流对象
3.使用OutputStream获取网络字节输出流对象中的方法write,给客户端发送数据
4.使用Socket对象中的方法getInputStream获取网络字节输入流对象
5.使用InputStream获取网络字节输入流对象中的方法read,读取服务器回写的数据
6.释放资源(Socket)
创建Socket对象,客户端就会和服务器进行三次握手,建立连接
服务器已经启动:连接建立成功,就可以通行了
服务器没有启动:会抛出连接异常 ConnectException: Connection refused: connect
*/
public class TCPClient {
public static void main(String[] args) throws IOException {
//1.创建客户端Socket对象,封装服务器的IP地址和端口号
Socket socket = new Socket("127.0.0.1",8888);
//2.使用Socket对象中的方法getOutputStream获取网络字节输出流对象
OutputStream os = socket.getOutputStream();
//3.使用OutputStream获取网络字节输出流对象中的方法write,给客户端发送数据
os.write("你好服务器".getBytes());
//4.使用Socket对象中的方法getInputStream获取网络字节输入流对象
InputStream is = socket.getInputStream();
//5.使用InputStream获取网络字节输入流对象中的方法read,读取服务器回写的数据
byte[] bytes = new byte[1024];
int len = is.read(bytes);
System.out.println(new String(bytes,0,len));
//6.释放资源(Socket)
socket.close();
}
}
----------------------------------------------------------
创建TCP通信的服务器端
/*
创建TCP通信的服务器端:
和客户端经过3次握手连接逻辑连接;接收客户端发送的数据,给客户端回写一个数据
表示服务器的类:
jva.net.ServerSocket:此类实现服务器套接字。
构造方法:
ServerSocket(int port) 创建绑定到特定端口的服务器套接字。
成员方法:
Socket accept() 侦听并接受到此套接字的连接。
启动服务器之后,有客户端请求服务器,就可以使用accept方法,监听并获取到请求的客户端Socket对象
实现步骤:
1.创建服务器ServerSocket对象,和系统要指定的端口号
2.使用accpet方法,监听并获取请求的客户端对象Socket
3.使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
4.使用网络字节输入流InputStream对象中的方法read,读取客户端发送的数据
5.使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputStream对象
6.使用网络字节输出流OutputStream对象中的方法write,给客户端回写数据
7.释放资源(Socket,ServerSocket)
*/
public class TCPServer {
public static void main(String[] args) throws IOException {
//1.创建服务器ServerSocket对象,和系统要指定的端口号
ServerSocket server = new ServerSocket(8888);
//2.使用accpet方法,监听并获取请求的客户端对象Socket
Socket socket = server.accept();
//3.使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//4.使用网络字节输入流InputStream对象中的方法read,读取客户端发送的数据
byte[] bytes = new byte[1024];
int len = is.read(bytes);
System.out.println(new String(bytes,0,len));
//5.使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
//6.使用网络字节输出流OutputStream对象中的方法write,给客户端回写数据
os.write("收到,谢谢".getBytes());
//7.释放资源(Socket,ServerSocket)
socket.close();
server.close();
}
}