本文只是粗略的讲解了一下有关线程的问题,讲的并不是非常的完全。还有很多可以写,但是万般头绪慢慢来,刚开始写只是列了一个大纲,然后越写越多,发现打不住头了,只写了一些线程的概念和实现的方法,线程的作用什么的。匿名内部线程什么的。写的时候在网上也找了部分自己感觉好的解释和例子。后面在开章写线程的同步,异步等问题。本文如果发现有问题,欢迎留言指正。感谢!
讲线程之前先讲一个进程的概念:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和 调度的基本单位,是操作系统结构的基础。简单点说就是 一个应用程序可以理解成就是一个进程。
一个进程包含无数个线程,每个线程分别实现该应用程序的不同功能。下面就从最基本的线程讲起。
什么是线程?
- 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
- 一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。
- 每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
什么是多线程(Multithreading)?
多线程就是为了使得多个线程并行的工作以完成多项任务,以提高系统的效率。线程是在同一时间需要完成多项任务的时候被实现的。
什么是并行,并发和高并发?
1.并行简单点说:同时运行多条线程,就是并行 例如:两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU,现在的电脑基本上都支持。)
2.并发 :在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。
3.高并发简单点说:就是在同一时刻不同用户访问同一资源的问题,专业一点的说法就是在同一时刻有多个线程访问了同一个数据资源。
想了解怎么解决高并发,可以看看我的这篇文章 https://blog.csdn.net/weixin_44078196/article/details/86710363
Java程序运行原理:
- Java命令会启动java虚拟机(JVM),等于启动了一个应用程序,也就是启动了一个进程。
- 该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法
- 一个应用程序有且只有一个主线程,程序员不能New主线程,可以New子线程。
JVM启动的是多线程吗?
- JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。
- main方法的代码执行的位置就是在主线程(路径)
- 一个进程有多个线程
- finalize()这个方法在子线程(垃圾回收线程)执行
/*JVM的启动是多线程的吗?*/
public class Demo01 {
public static void main(String[] args) {
System.out.println("AAAAA");
System.out.println("BBBBB");
System.out.println("CCCCC");
System.out.println("DDDDD");
//打印线程名称
System.out.println(Thread.currentThread());//主线程
for(int i = 0;i<2;i++){
new Student();
System.gc();//启动垃圾回收
}
}
}
class Student{
//被垃圾回收器回收时,会调用
//对象从内存释放时,会调用
@Override
protected void finalize() throws Throwable {
// TODO Auto-generated method stub
System.out.println("student 被回收了...");
//打印线程名称
System.out.println(Thread.currentThread());//子线程
}
}
什么时候使用多线程?
当一个进程中多个任务可以并行执行时,可以为每个任务启动一个线程。
线程适用范围:
- 服务器中的文件管理或通信控制
- 前后台处理
- 异步处理
线程的作用:(一般通过同步,异步,线程池来实现)
有计划,有顺序的执行程序的各项功能,优化程序的执行过程,减少死机的可能。(开单章再说)
线程的实现方法:(具体实现方法在下面)
- 继承Thread类,并重写run函数
- 实现Runnable接口,并重写run函数
原因: 因为java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时在用继承Thread类方法来创建线程显然不可能java设计者们提供了另外一个方式创建线程,就是通过实现Runnable接口来创建线程。(也有人说线程的实现方法是4个,另外2种为:实现Callable接口通过FutureTask包装器来创建Thread线程、使用ExecutorService、Callable、Future实现有返回结果的多线程。)
什么是线程池?
线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程。线程池中线程的数量通常完全取决于可用内存数量和应用程序的需求。然而,增加可用线程数量是可能的。线程池中的每个线程都有被分配一个任务,一旦任务已经完成了,线程回到池子中并等待下一次分配任务。
为什么需要线程池?
- 线程池改进了一个应用程序的响应时间。由于线程池中的线程已经准备好且等待被分配任务,应用程序可以直接拿来使用而不用新建一个线程。
- 线程池节省了CLR 为每个短生存周期任务创建一个完整的线程的开销并可以在任务完成后回收资源。
- 线程池根据当前在系统中运行的进程来优化线程时间片。
- 线程池允许我们开启多个任务而不用为每个线程设置属性。
- 线程池允许我们为正在执行的任务的程序参数传递一个包含状态信息的对象引用。
- 线程池可以用来解决处理一个特定请求最大线程数量限制问题。
影响设计一个多线程应用程序的因素有:
- 一个应用程序的响应时间。
- 线程管理资源的分配。
- 资源共享。
- 线程同步。
===========线程的实现方法============
方式一、继承Thread
使用步骤:
- 定义类继承Thread
- 重写run方法
- 把新线程要做的事写在run方法中
- 创建线程对象
- 开启新线程, 内部会自动执行run方法
public class testThread {
public static void main(String[] args) {
/*主线程,程序员不能创建,程序员只能创建子线程*/
//1.创建子线程对象
MyThread t1 = new MyThread();
/**不能通过下面的方式来执行任务
* 因为这种方式中的任务是在主线程执行的*/
//t1.run();
//2.正确的执行任务的方式,调用start,内部会开启新线程,调用run方法
t1.start();
//3.再创建子线程
MyThread t2 = new MyThread();
t2.start();
//4.循环创建子线程
for(int i=0;i<10;i++){
MyThread th = new MyThread();
th.start();
}
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println("银行信用卡还款短信任务..." + Thread.currentThread());
System.out.println("线程名称" + this.getName());
}
}
方式二、实现Runnable接口
实现步骤:
- 定义类实现Runnable接口
- 实现run方法
- 把新线程要做的事写在run方法中
- 创建自定义的Runnable的子类对象,创建Thread对象传入Runnable
- 调用start()开启新线程, 内部会自动调用Runnable的run()方法
public class TestRunnable {
public static void main(String[] args) {
/* 线程实现的方式 (2) - 定义类实现Runnable接口
//1.创建runable对象
BankTask task = new BankTask();
//2.创建Thread对象
Thread t1 = new Thread(task);
//3.启动线程
t1.start();
//4.再开启2个线程
Thread t2 = new Thread(task);
t2.start();
Thread t3 = new Thread(task);
t3.start();
}
}
class BankTask implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("银行储蓄卡自动结算利息任务..." + Thread.currentThread());
//System.out.println("线程名称:" + this.getName());
System.out.println("线程名称:" +Thread.currentThread().getName());
}
}
两种方式的区别
区别:
- 继承Thread : 由于子类重写了Thread类的run(), 当调用start()时直接找子类的run()方法
- 实现Runnable : 构造函数中传入了Runnable的引用, 有个成员变量记住了它, 调用run()方法时内部判断成员变量Runnable的引用是否为空。
继承Thread
- 好处是:可以直接使用Thread类中的方法,代码简单
- 弊端是:如果已经有了父类,就不能用这种方法(Java只能单继承)
实现Runnable接口
- 好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,代码更灵活
- 弊端是:不能直接使用Thread中的方法,需要先获取到线程对象后,才能得到Thread的方法,代码复杂
匿名内部类线程实现方式:
public static void main(String[] args) {
//匿名内部类实现线程的两种方式
/*Thread t1 = new Thread(){
@Override
public void run() {
System.out.println("任务1...." + Thread.currentThread());
}
};
t1.start();*/
new Thread(){
public void run() {
System.out.println("任务1...." + Thread.currentThread());
};
}.start();
/*Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("任务2...." + Thread.currentThread());
}
});
t2.start();*/
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("任务2...." + Thread.currentThread());
}
}).start();
}
**************************************************************************************************************************************************************
贴一些个人感觉不错的文章。
https://blog.csdn.net/dong_19890208/article/details/50474913 线程池作用及Executors方法讲解
https://www.cnblogs.com/felixzh/p/6036074.html 线程实现的4种方式
https://blog.csdn.net/AlbenXie/article/details/78507361 什么是高并发