文章目录
一、基本概念的了解:
1、程序、进程、线程
程序:完成特定任务,用编程语言聪明些的一组指定的集合,即一段静态的代码。
进程(process):是程序的一次执行过程,或是正在运行的一个程序。是一个动态过程:有它自身的产生、存在、消亡 的过程(生命周期)。
- 进程作为资源分配的单位,系统在运行时会为每个而进程分配不同的内存区域。
线程(thread):进程可以进一步细化为线程,即一个程序内部的一条或多条执行路径
- 在java程序员中,是支持多线程的,一个进程可同一时间并行执行多个线程
- 线程作为调度和执行的单位,每个线程久拥有独立的运行路栈和程序技术器(PC计数器), 线程的切换开销小。
- 一个进程值送的多个线程共享相同的内存单元、内存地址空间
- 他们中同一个堆中分配对象,可以访问相同的变量和对象, 这就使线程间通讯更方便高效,但多个线程操作共享的系统资源就会带来安全隐患。
2、单核CPU和多核CPU的理解
- 在单核CPU中,初始是一种假的多线程,应为在同一个时间内,也只能执行一个线程的任务,只是CPU时间单元特别短,因此感觉不出来,像是再同时执行一样。
- 现在的CPU大多都是多核的,这样能够更好发挥多线程的效率
一个java应用程序java.exe在执行空的过程,至少有三个线程,main()线程、 gc()垃圾回收线程、异常处理线程。
3、并行与并发:
- 并行:同一时刻,有多条指令在多个处理器上同时执行, 多个CPU同时执行多个任务,比如多个人同时做不同的事情。
- 并发:在同一个时刻,只能有一条指令执行,但有多个进程指令被快速轮换执行,使得宏观上具有多个进程同时执行的效果。 一个CPU(采用时间片轮询的方式)同时执行多个任务, 比如秒杀,多个人同时做一件事情。
4、使用多线程的优点:
- 提过应用程序的响应,可增强用户体验。
- 提高计算前期系统CPU的利用率。
- 改善程序结构,将长而且复杂的进程分为多个线程,独立运行,利于理解和修改。
private native void start0()//使用C/C++实现的本地方法
- 而不是直接调用run,这样是在当前线程中调用run方法,而不会开启一个线程。
二、多线程创建
1、继承自Thread类,然后重写run方法。
// 第一个类:SubThread ,继承Thread类
class SubThread extends Thread{
// 重写Thread的run方法
@Override
public void run() {
String threadName = getName();
for(int i =0; i<=100 ;i++){
System.out.println(threadName+":"+i);
}
}
}
// 新建并开启一个线程
public class ThreadDemo01 {
public static void main(String[] args) {
// 创建实例
SubThread thread1 = new SubThread();
// 设置线程的名称,如果没有设置的话会有默认的名字
thread1.setName("继承Thread类");
// 调用start方法,只有调用start()方法才是开启一个线程,
// 如果调用的是run()方法,只是一个一般的方法调用。
thread1.start();
System.out.println(Thread.currentThread().getName());
}
}
2、实现方式2:实现Runnable接口,对run方法进行实现。
// 第一个类:实现Runnable接口,对run()方法进行实现
class RunnableImpl implements Runnable{
@Override
public void run() {
// 这里是获取线程的名称
String threadName = Thread.currentThread().getName();
for(int i =0; i<=100 ;i++){
System.out.println(threadName+":"+i);
}
}
}
// 第二个类:
public class ThreadDemo02 {
public static void main(String[] args){
// 创建实现类的对象
Runnable runnable = new RunnableImpl();
// 将runnable作为参数传到Thread类的构造器值搜,创建Thread的对象。
Thread thread2 = new Thread(runnable);
thread2.setName("实现Runnable接口");
// 调用start方法
thread2.start();
}
}
3、创建线程的方式3:实现Callable接口,对call()方法进行实现:
// 第一个类:实现Callable接口,然后对call进行实现
class CallableImpl implements Callable{
/**
* 可以有返回值,
* 可以抛出异常
* 支持泛型的返回值
* 需要借助FutureTest类,比如获取返回值结果
*
* */
@Override
public Object call() throws Exception {
int sum = 0;
String threadName = Thread.currentThread().getName();
for(int i =0; i<=100 ;i++){
System.out.println(threadName+":"+i);
sum += i;
}
return sum;
}
}
// 第二个类:
public class ThreadDemo02 {
public static void main(String[] args){
// 创建CallableImpl 的实例
CallableImpl callable = new CallableImpl();
// 借助FutureTask类,将实例callable 作为FutureTask构造方法的参数
FutureTask futureTask = new FutureTask(callable);
// 创建Thread的实例
Thread thread3 = new Thread(futureTask);
// 设置线程的名称
thread3.setName("实现Callable接口");
// 开启一个线程
thread3.start();
}
}
4、实现多线程的方式4:使用线程池创建线程
背景:经常创建和销毁、使用量特别大的资源, 比如并发情况下的子线程,对性能影响很大。
思路:提前造好多个线程,放入线程池中,使用的时候直接获取, 使用完后返回翅中,可以避免频繁创建、销毁,从而达到重复利用
好处:
- 提高响应速度(减少了创建新线程的时间)
- 减低资源消耗:(重复利用线程池中线程,不需要每次创建)
- 便于线程管理
线程的相关属性
属性:
- corePoolSize:核心池的大小
- maximumPoolSize:最大线程数
- keepAliveTime:线程没有任务最多保持多长时间会终止。
public class ThreadDemo04{
public static void main(String[] args){
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service2 = (ThreadPoolExecutor)service;
//设置线程池的属性
/* 这里的setCorePoolSize的数字
不能大于 Executors.newFixedThreadPool(10) 中的数字
*/
service2.setCorePoolSize(5);
// 执行指定的线程操作,需要实现Runable接口或者Callable接口实现类的对象
service2.execute(new RunnableImpl());
service2.submit(new CallableImpl());
service.shutdown();
}
}
三、启动线程:start()
- 启动线程使用start方法
- start()–>启动一个线程,最终调用本地方法start0()