这三章节基本知识点不是很多,重点是代码的编写。
接下来我将以三道例题来分别讲解这三个知识点
例:
创建一个模拟铁路售票系统,要求创建10个线程模拟10个售票点,每个售票点不停地卖票, 每张票有一个 id(从 0 开始不断增加)。
要求:
1) 每个售票点销售的票 id 不能重复。
2) 一共只有 200 张火车票,售完为止。
3) 程序显示的结果类似于:
售票点 1 正在售出火车票 No.1
售票点 2 正在售出火车票 No.2
售票点 1 正在售出火车票 No.3
售票点 5 正在售出火车票 No.4
售票点 8 正在售出火车票 No.5
售票点 1 正在售出火车票 No.6
售票点 3 正在售出火车票 No.7
…
售票点 9 正在售出火车票 No.200
售票点 1 售罄
售票点 2 售罄
...
这个例题涉及的相关知识面还挺广的。
代码中有疑问的地方,请自行移步学习:菜鸟教程:Java多线程编程
package test;
import java.util.concurrent.atomic.AtomicInteger;
//首先将这个项目定义为一个多线程类
//其次,将所有线程都会用到的变量定义在进程中,且加上final关键字,防止线程私自改变这些变量;
//然后,我们给每个线程定义了ThreadID来区分不同的线程
//最后,synchronized关键字的使用,同步加锁。
//学过操作系统的同学应该都知道加锁的概念。
//这个关键字就是:我在使用某个对象的时候给这个对象加锁,其余线程全部不能使用这个变量,
//等我释放之后其他线程才能使用。
//使用这个关键字,来预防多个线程同时卖出同一张票的情况。
public class MultiThread implements Runnable {
public static final int Totaltickets=200;
/*AtomicInteger对象,本质上还是个int,但是这个对象有一个自增函数。因为该对象加上了final关键
字,而我们又想让其+1,所以就采用AtomicInteger关键字来实现。*/
public static final AtomicInteger currentTicket=new AtomicInteger(0);
public static final Object lock=new Object();
private final int threadID;
//构造函数,指定ThreadID,且添加final关键字,这个值是进程赋予线程的。线程不能对其进行改变
public MultiThread(int Id){
this.threadID=Id;
}
@Override
public void run(){
while(true){
try{
synchronized(lock){
int currentTicket=MultiThread.currentTicket.getAndIncrement();
//得到当前的票数
if(currentTicket<MultiThread.Totaltickets){
System.out.println("售票口"+threadID+" 售出票号:"+currentTicket);
}
else{
System.out.println("售票口"+threadID+"已售罄");break;
}
}
Thread.sleep(100);
}catch (Exception e){e.printStackTrace();}
}
}
public static void main(String[] args) {
//创建十个线程并运行
for (int i = 0; i < 10; i++) {
new Thread(new MultiThread(i)).start();
}
}
}
例:
JDBC大家应该用的都比教多,随便写个JavaWeb或者数据库的实验都得用到。但是这里我们直接使用Java代码与数据库进行交互,不引入mybatis等其他的框架。这里可能题目有点小问题,应该是在数据库中创建数据表bookstore。
大致流程:
(1)连接数据库
连接数据库,使用DriverManager对象连接数据库,
通过connection创建pre对象,
(2)插入数据
使用preparedstatement的executeQuary()函数插入数据
(3)读取数据
通过connection创建statement对象。
通过statement对象执行sql语句
查询语句的返回值是ResultSet对象
遍历rs对象进行输出
下列代码不保证百分百运行,因为可能还需要一些额外的数据库的文件配置。
package test;
import java.sql.*;
public class JdbCode {
public static void main(String[] args) {
Class.forName("com.mysql.cj.jdbc.Driver");
try{
//连接数据库
Connection con= DriverManager.getConnection("jdbc:mysql://localhost:3306?DB_NAME&&username='root'&&password='root'");
//设置执行语句
String create= "CREATE TABLE IF NOT EXISTS bookstore (VARCHAR(20) store_name PRIMARY KEY,INT Sales, VARCHAR(20) Date)";
//创建stetement对象,建立数据表
Statement createTable=con.createStatement();
createTable.executeQuery(create);
PreparedStatement pre=con.prepareStatement("INSERT INTO bookstore (VARCHAR(20) store_name,INT Sales, VARCHAR(20) Date) VALUS(?,?,?)");
//使用preparedstatement对象插入数据
insert("Los Angeles",1500,"1999-01-09",pre);
insert("San Diego",250,"1999-01-01",pre);
insert("Los Angeles",300,"1999-01-02",pre);
insert("Boston",700,"1999-01-05",pre);
//创建statement对象,读取数据表中的数据,返回值为ResultSet
ResultSet rs=con.createStatement().executeQuery("SELETC * FROM bookstore");
while(rs.next()){
String name=rs.getString(1);
int sale=rs.getInt(2);
String date=rs.getString(3);
System.out.printf("10_s%",name);
System.out.printf("10_d%",sale);
System.out.printf("10_s%",date);
}
}catch (Exception e){e.printStackTrace();}
}
public static void insert(String store_name,int Sales,String Date,PreparedStatement pre) throws SQLException {
pre.setString(1,store_name);
pre.setInt(2,Sales);
pre.setString(3,Date);
pre.executeBatch();
pre.clearParameters();
}
}
网络编程起码要有两个类,一个服务端,一个客户端。而且服务端多数情况下要写为多线程,以同时服务多个客户端。甚至可能一个服务端要分两个线程,以便在接受消息的时候发送消息。所以服务端一般在main函数里面创建socket对象,表示监听接口,可以创建多个。然后通过多线程来同步开启消息的接受和发送。
先简单说下流程吧,
(1)服务器创建一个ServerSocket对象并使用accept()函数监听一个端口;当客户端访问该端口时,accept函数返回一个Socket对象。
(2)客户端使用Socket对象直接根据URL和端口号连接服务端。
(3)连接成功后,客户端和服务端之间就可以通过socket对象进行通信。socket对象同时具有输入输出流,且socket对象可以确定本地以及对方的Adress。
例题:
使用控制台,写一个内网聊天工具,能够同时和另一个人接受并发送信息。(已知双方 的 ip 地址和端口号,不要求同时与多人聊天)注:(这一段代码实现了多人同步聊天,但是只能是服务器同步和多个客户端聊天,但是需要多建立几个服务器才可。这里只建立了一个)
服务端:
package chatApp;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
private static int MAX_THREADS=10;
public static int CURRENT_THREAD=0;
public static void main(String[] args) throws IOException {
ServerSocket serverSocket=new ServerSocket(1234);
//在服务器程序启动时,它将在端口1234上创建一个ServerSocket对象,用于监听来自客户端的连接请求。
// 一旦有客户端尝试连接到该端口,ServerSocket将接受连接并返回一个Socket对象,该对象可用于与客户端进行通信。
// 在这之后,服务端可以使用该Socket对象与客户端进行数据交换。
System.out.println("等待客户端连接......");
Socket clientSocket=serverSocket.accept();
System.out.println("客户端已连接");
//启动发送消息的线程;
new Thread(new SendMessage(clientSocket)).start();
//启动接受信息的线程
new Thread(new ReceiveMessage(clientSocket)).start();
}
static class ReceiveMessage implements Runnable{
//接收消息的线程
private Socket socket;
public ReceiveMessage(Socket socket){
this.socket=socket;
}
@Override
public void run() {
try {
//通过socket对象获取输入流,这是用于从客户端接收数据的输入流。
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line=bufferedReader.readLine();
//这个循环会不断地从输入流中读取新的行,然后在控制台上打印服务器收到的消息。当客户端关闭连接时,
while ((line = bufferedReader.readLine()) != null) {
System.out.println("客户端: " + line);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
static class SendMessage implements Runnable{
//发送消息的线程
private Socket socket;
public SendMessage(Socket socket){
this.socket=socket;
}
@Override
public void run() {
try {
//获取到与客户端连接的输出流,这里用于向客户端发送数据。
PrintWriter printWriter = new PrintWriter(socket.getOutputStream(),true);
//写入流,用于从控制台读取信息
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader( System.in));
String line;
//读取控制台输入的内容
while ((line = bufferedReader.readLine()) != null) {
//然后将控制台读取的内容通过socket的OutputStream输出给客户端
printWriter.println(line);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
客户端:
package chatApp;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class Clienter {
public static void main(String[] args) {
try {
Socket socket = new Socket("localhost", 1234);
// 创建并启动接收消息的线程
new Thread(new ReceiveMessage(socket)).start();
// 创建并启动发送消息的线程
new Thread(new SendMessage(socket)).start();
} catch (IOException e) {
e.printStackTrace();
}
}
static class ReceiveMessage implements Runnable {
private Socket socket;
public ReceiveMessage(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println("服务器: " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
static class SendMessage implements Runnable {
private Socket socket;
public SendMessage(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
String line;
while ((line = consoleReader.readLine()) != null) {
writer.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}