进程跟线程的概念
进程:
- 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
线程:
- 线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
单线程
- 单线程也可以理解为是串行任务:一次只执行一个
/**
* 普通类
* 模拟串行任务
* 原理:执行完一个方法之后才能执行另一个方法
* @author LingDu
*/
public class Demo {
public static void test1() {
for (int i = 0; i < 5; i++) {
System.out.println("不快乐!" + i);
}
}
public static void test2() {
System.out.println("------快乐!");
}
public static void main(String[] args) {
test1();
test2();
}
}
执行完第一个方法之后才会执行第二个。
多线程
- 多线程可以理解为是并行:一次执行多个(原理还是cpu分时)
使用多线程常用的方法:
继承Thread类
- 重写run()方法
/**
* 通过继承得到一个多线程的类。
* 多线程可以并行
* @author Administrator
*
*/
public class RichMan extends Thread {
@Override
public void run() {
for(int i =0;i<5;i++){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(this.getName() +":" + i);
}
}
/**
* 空的构造方法
*/
public RichMan(){
}
/**
* 指定线程名称的构造方法
* @param name
*/
public RichMan(String name){
this.setName(name);
}
public static void main(String[] args) {
//亲儿子
RichMan wangsicong = new RichMan("亲儿子");
wangsicong.start();
//私生子
RichMan otherChild = new RichMan("私生子");
otherChild.start();
}
}
实现Runnable接口
- 重写run()方法
/**
* 实现接口 Runnable
* implements Runnable
* 重写run()方法
* @author LingDu
*
*/
public class RunnableImplDemo implements Runnable {
@Override
public void run() {
for(int i = 0;i<10; i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(i);
}
}
public static void main(String[] args){
RunnableImplDemo t1 = new RunnableImplDemo();
new Thread(t1).start();
}
}
匿名内部类实现多线程
/**
* 匿名内部类实现 new Runnable()
* 重写run()方法
* @author LingDu
*/
public class RunnableImplDemo2 {
public static void test1() {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(500);
System.out.println(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t1.start();
}
public static void main(String[] args) {
test1();
}
}
实战
多线程实现并发访问
之前我发过一篇使用Socket 套接字 来实现的服务端的程序:
实现的功能很单一,每次只能连接一个用户,现在我们就利用多线程来创建一个可以多人连接的Socket服务端
功能:
- 模拟自动回复机器人,可以同时多人连接
需求:
- 1、允许多个客户端连接上来
- 2、接收客户端的连接请求。
- 3、当客户端连接上我们的端口之后发送欢迎语
- 4、当客户端发送一个消息给服务端时,可以自动回复信息
SocketThread类,继承自Thread ,用于实现功能
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
/**
* 2017.2.13
* SocketThread类
* 功能:接收用户信息并且自动回复信息
* @author LingDu
*/
public class SocketThread extends Thread{
Socket socket = null;
/**
* 带参的构造方法
* 传入一个Socket对象
* @param socket
*/
public SocketThread(Socket socket) {
super();
this.socket = socket;
}
/**
* 重写的run()方法
*/
@Override
public void run() {
userConnection();
}
/**
* 用户连接的方法
*/
public void userConnection(){
try {
//打印出连接的用户
System.out.println("连接一个用户:" + socket.getInetAddress());
//拿到用户的输出流
PrintWriter pw = new PrintWriter(socket.getOutputStream());
//用户连接上来后先发送欢迎语
pw.println("欢迎光临!");
pw.flush();
//拿到用户的输入流
Scanner scanner = new Scanner(socket.getInputStream());
//如果有下一行数据
while(scanner.hasNextLine()){
//拿到用户发送过来的消息
String msg = scanner.nextLine();
//如果是 “exit”
if("exit".equalsIgnoreCase(msg)){
//跳出循环
break;
}
//返回一个自动处理过的消息
String reutrnMsg = ServerSocketDemo.getRetuenMsg(msg);
//将消息返回给用户
pw.println(reutrnMsg);
//刷新
pw.flush();
}
socket.close();
pw.close();
scanner.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
ServerSocketDemo类,用于开启服务
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class ServerSocketDemo {
/**
* 开启程序
* 功能:允许多人连接
*/
public static void threadStart(){
ServerSocket serverSocket = null;
try {
//创建服务端端口8888
serverSocket = new ServerSocket(8888);
System.out.println("服务已启动!");
//获取用户的连接
while(true){
Socket socket = serverSocket.accept();
//创建SocketThread对象,将连接传入进去
SocketThread st = new SocketThread(socket);
//启动线程
st.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 这里模拟自动回复机器人的功能
* 1、传入用户发送过来的消息
* 2、简单判断之后将结果返回
* @param msg 用户的消息
* @return returnMsg
*/
public static String getRetuenMsg(String msg){
if(msg == null){
msg = "";
}
String returnMsg = "";
//如果消息中包涵 “在吗” 字段
if(msg.contains("在吗")){
returnMsg = "亲,所有商品都有货,小二24小时在线";
}else if(msg.contains("发货")){
returnMsg = "亲,我们立即发货";
}else if(msg.contains("包邮")){
returnMsg = "亲,我们所有商品全部包邮,除了西藏/新疆";
}else{
returnMsg = "亲,你大概不是在玩真的";
}
return returnMsg;
}
public static void main(String[] args){
threadStart();
}
}
使用telnet进行连接测试
- 此时服务端检测到一个用户,会开启一个线程,并将欢迎语发送给客户端
客户端收到欢迎语
接下来测试一个自动回复的功能
多人连接的时候会创建多个线程
这样一个简单的多并发程序就做好了
模拟多人抢红包程序
功能:
- web端:创建红包网页,用户访问时返回一个1-10元的随机金额
- java 程序:利用多线程模拟多个用户访问页面,拿到返回的随机金额
Web端
- 创建一个 Dynamic Web Project 项目
Servlet 容器:
/**
* RP.java
* 生成一个随便返回到页面
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Math.random();
int max=10;
int min=1;
Random random = new Random();
int s = random.nextInt(max)%(max-min+1) + min;
response.getWriter().append(s+"");
}
开启Tomcat服务器
Java程序
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Scanner;
/**
* 2017.2.13
* 使用多线程获取红包
* @author LingDu
*
*/
public class ThreadGetRedPackage extends Thread {
@Override
public void run() {
System.out.println(getRedPackage());
}
// 无参的构造方法
public ThreadGetRedPackage() {
super();
}
// 带参数的构造方法,传入一个名字
public ThreadGetRedPackage(String name) {
super(name);
}
// 返回网页内容
public String getRedPackage() {
String returnContent = "";
try {
URL url = new URL("http://192.168.20.68:8080/firstweb/RP");
try {
Scanner sc = new Scanner(url.openStream());
String content = "";
while (sc.hasNextLine()) {
content += sc.nextLine();
}
returnContent = this.getName() + "------>抢到:" + content + "元红包";
sc.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
return returnContent;
}
public static void main(String[] args) {
//创建三个线程同时访问红包页面
ThreadGetRedPackage t1 = new ThreadGetRedPackage("t1");
ThreadGetRedPackage t2 = new ThreadGetRedPackage("t2");
ThreadGetRedPackage t3 = new ThreadGetRedPackage("t3");
t1.start();
t2.start();
t3.start();
//主线程再模拟5个机器人
for (int i = 0; i < 5; i++) {
ThreadGetRedPackage robot = new ThreadGetRedPackage("机器人" + i);
robot.start();
}
}
}
这样就实现了一个利用多线程从网上拿红包的程序
当然,程序是不完整的
有关多线程的更多知识(例如线程同步、线程安全、死锁问题等…)都会在后续文章中一 一详解
总结:
多线程的优缺点
优点:
- 1、 资源利用率更好
- 2、 程序响应更快
缺点:
- 1、线程太多会降低系统的运行性能
多线程应用场景
- 1、压力测试时,会用到多线程。
- 2、服务器编程时,会用到多线程。
- 3、使用监听器时,可能会用到多线程。
- 4、跑JOB时,可能会用到多线程。
那些地方适合使用多线程
- 1、耗时或大量占用处理器的任务阻塞用户界面操作。
- 2、各个任务必须等待外部资源 (如远程文件或 Internet连接)。