我们在之前,学习的程序在没有跳转语句的前提下,都是由上至下依次执行,那现在想要设计一个程序,边打游戏边听歌,怎么设计?
要解决上述问题,咱们得使用多进程或者多线程来解决
进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
线程:是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
注意:下面内容为了解知识点
Java使用 java.lang.Thread 类代表线程,所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码。Java使用线程执行体来代表这段程序流。Java中通过继承Thread类来创建并启动多线程的步骤如下:
自定义线程类:
public class MyThread extends Thread {
/*** 重写run方法,完成该线程执行的逻辑 */
@Override public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("自定义线程正在执行!"+i);
}
}
}
测试类:
public static void main(String[] args) {
Mythread mythread = new Mythread();
mythread.start();
for (int i = 0; i < 200; i++) {
System.out.println("main主线程"+i+" "+Thread.currentThread().getName());
}
}
程序启动运行main时候,java虚拟机启动一个进程,主线程main在main()调用时候被创建。随着调用mt的对象的start方法,另外一个新的线程也启动了,这样,整个应用就在多线程下运行。
通过这张图我们可以很清晰的看到多线程的执行流程,那么为什么可以完成并发执行呢?我们再来讲一讲原理。
多线程执行时,到底在内存中是如何运行的呢?以上个程序为例,进行图解说明:
多线程执行时,在栈内存中,其实每一个执行线程都有一片自己所属的栈内存空间。进行方法的压栈和弹栈。
当执行线程的任务结束了,线程自动在栈内存中释放了。但是当所有的执行线程都结束了,那么进程就结束了。
public class MyThread extends Thread{
public void run(){
System.out.println("线程名字:"+super.getName()); }
}
测试类:
public class Demo {
public static void main(String[] args) {
//创建自定义线程对象
MyThread mt = new MyThread();
//设置线程名字
mt.setName("旺财");
//开启新线程
mt.start();
}
}
注意:线程是有默认名字的,如果我们不设置线程的名字,JVM会赋予线程默认名字Thread-0,Thread-1。
public static void main(String[] args){
Thread t = Thread.currentThread();
System.out.println(t.getName());
}
采用java.lang.Runnable也是非常常见的一种,我们只需要重写run方法即可。
步骤如下:
public class MyRunnable implements Runnable{
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
public class Demo {
public static void main(String[] args) {
//创建自定义类对象 线程任务对象
MyRunnable mr = new MyRunnable();
//创建线程对象
Thread t = new Thread(mr);
t.start();
for (int i = 0; i < 20; i++)
{
System.out.println("main " + i);
}
}
}
通过实现Runnable接口,使得该类有了多线程类的特征。run()方法是多线程程序的一个执行目标。所有的多线程代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。
在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码。
实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是继承Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程
编程的基础。
如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
总结:
实现Runnable接口比继承Thread类所具有的优势:
使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。
使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法:
public class NoNameInnerClassThread {
public static void main(String[] args) {
// new Runnable(){
// public void run(){
// for (int i = 0; i < 20; i++) {
// System.out.println("热巴:"+i);
// }
// }
// };
//---这个整体 相当于new MyRunnable()
Runnable r = new Runnable(){
public void run(){
for (int i = 0; i < 20; i++)
{
System.out.println("热巴:"+i); }
}
};
new Thread(r).start();
for (int i = 0; i < 20; i++) {
System.out.println("娜扎:"+i);
}
}
}
B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等
两种架构各有优势,但是无论哪种架构,都离不开网络的支持。网络编程,就是在一定的协议下,实现两台计算机的通信的程序。
通信的协议还是比较复杂的, java.net 包中包含的类和接口,它们提供低层次的通信细节。我们可以直接使用这些类和接口,来专注于网络程序开发,而不用考虑通信的细节。
java.net 包中提供了两种常见的网络协议的支持:
完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛,例如下载文件、浏览网页等。
ipconfig
ping 空格 IP地址
ping 220.181.57.216
ping www.baidu.com
网络的通信,本质上是两个进程(应用程序)的通信。每台计算机都有很多的进程,那么在网络通信时,如何区分这些进程呢?
如果说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的进程(应用程序)了。
利用 协议 + IP地址 + 端口号 三元组合,就可以标识网络中的进程了,那么进程间的通信就可以利用这个标识与其它进程进行交互。
TCP通信能实现两台计算机之间的数据交互,通信的两端,要严格区分为客户端(Client)与服务端(Server)。
两端通信时步骤:
在Java中,提供了两个类用于实现TCP通信程序:
Socket 类:该类实现客户端套接字,套接字指的是两台设备之间通讯的端点.
Socket client = new Socket("127.0.0.1", 6666);
public InputStream getInputStream() : 返回此套接字的输入流。
public OutputStream getOutputStream() : 返回此套接字的输出流。
public void close() :关闭此套接字。
public void shutdownOutput() : 禁用此套接字的输出流。
ServerSocket 类:这个类实现了服务器套接字,该对象等待通过网络的请求。
ServerSocket server = new ServerSocket(6666);
TCP通信分析图解
public class ClientTCP {
public static void main(String[] args) throws Exception {
System.out.println("客户端 发送数据");
// 1.创建 Socket ( ip , port ) , 确定连接到哪里.
Socket client = new Socket("localhost", 6666);
// 2.获取流对象 . 输出流
OutputStream os = client.getOutputStream();
// 3.写出数据.
os.write("你好么? tcp ,我来了".getBytes());
// 4. 关闭资源 .
os.close();
client.close();
}
}
public static void main(String[] args) throws IOException {
//创建一个服务端serverSocket对象,指定服务器端口号
ServerSocket serverSocket = new ServerSocket(6666);
//开启服务器,等待客户端来连接,当有客户端连接时,得到连接客户端Socket对象
System.out.println("服务器已经启动");
Socket accept = serverSocket.accept();
//通过客户端Socket对象,获取客户端发来的数据
InputStream in = accept.getInputStream();
byte [] buff = new byte[1024];
//读取客户端发来的数据
int len = in.read(buff);
System.out.println(new String(buff,0,len));
//关闭Socket对象
in.close();
out.close();
accept.close();
}
public class Client {
public static void main(String[] args) throws IOException {
//1.创建一个客户端Socket对象,指定要连接的服务器地址
Socket socket = new Socket("127.0.0.1", 6666);
//2.获取当前Socket对象的输出流,发送数据给服务器
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hello,server".getBytes());
//接收服务器发来的数据
InputStream inputStream = socket.getInputStream();
byte[] b = new byte[1024];
int read = inputStream.read(b);
System.out.println(new String(b,0,read));
//3.关闭客户端的Socket对象
outputStream.close();
inputStream.close();
socket.close();
}
}
public class Server {
public static void main(String[] args) throws IOException {
//创建一个服务端serverSocket对象,指定服务器端口号
ServerSocket serverSocket = new ServerSocket(6666);
//开启服务器,等待客户端来连接,当有客户端连接时,得到连接客户端Socket对象
System.out.println("服务器已经启动");
Socket accept = serverSocket.accept();
//通过客户端Socket对象,获取客户端发来的数据
InputStream in = accept.getInputStream();
byte [] buff = new byte[1024];
//读取客户端发来的数据
int len = in.read(buff);
System.out.println(new String(buff,0,len));
//返回数据给客户端
OutputStream out = accept.getOutputStream();
out.write("好的,你已经成功连接到我了".getBytes());
//关闭Socket对象
in.close();
out.close();
accept.close();
}
}
public class Client {
public static void main(String[] args) throws IOException {
//首先用字节输入流获取到要上传的文件
FileInputStream fis = new FileInputStream("filedemo/1.jpeg");
//定义客户端socket接收字节流数据
Socket socket = new Socket("127.0.0.1",6666);
OutputStream out = socket.getOutputStream();
byte [] bytes = new byte[1024];
int read;
while((read = fis.read(bytes))!=-1)
{
//通过Socket输出流发送给服务端
out.write(bytes,0,read);
}
socket.shutdownOutput();
System.out.println("文件发送完毕");
InputStream in = socket.getInputStream();
byte [] by = new byte[1024];
int read1 = in.read(by);
System.out.println(new String(by,0,read1));
fis.close();
in.close();
socket.close();
out.close();
}
}
public class Server {
public static void main(String[] args) throws IOException {
//服务端接收客户端传入的数据
ServerSocket serverSocket = new ServerSocket(6666);
System.out.println("服务器已经启动");
while (true)
{
Socket socket = serverSocket.accept();
//开启一个新线程与客户端数据交互
new Thread(new Runnable() {
@Override
public void run() {
try {
InputStream in = socket.getInputStream();
//定义字节输出流输出到指定文件
FileOutputStream fos = new FileOutputStream("img/"+System.currentTimeMillis()+".jpeg");
byte [] bytes = new byte[1024];
int len;
while ((len=in.read(bytes))!=-1)
{
fos.write(bytes,0,len);
}
OutputStream opt = socket.getOutputStream();
opt.write("文件上传成功".getBytes());
fos.close();
opt.close();
in.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
文件上传案例可以自己先做一遍再看答案哦~