线程
并发:
- 多个不同的软件同时运行
Windows/Linux等操作系统,同时管理多个软件并发执行
eclipse,notepad++,chrome,各类游戏等软件同时运行 - 一个软件可以被多个用户同时请求
多个浏览器用户可以同时请求淘宝,同时做结算操作/支付操作
等价于结算操作和支付操作在服务器端被多次运行 - 总结
多个软件同时运行
一个软件被运行多次
就是为了要CPU不停的工作,提高CPU的利用率
计算机是如何做到并发的:
- 前提:一台计算机,一块CPU,一个核
- 分析硬件CPU:
在CPU的某个时间点上只能有一个程序在执行
为了能做到并发,把CPU的时间分成若干时间片段
(时间片段的划分原则由操作系统来定义)
每个晓得时间片段只能执行一个进程
时间片到了就把程序强制离开CPU
再从内存中选出下一个程序继续执行,以此往复
看似在一段时间内十多个程序在同时运行
- 分析软件:
一个软件动辄几百兆或上G的内容
一个软件是不能整个存储到内存中的额
进程:把大的软件分割成多个小的程序段,此程序段可以称之为进程
多个大的程序要运行,先把程序分割成多个程序段
然后把每个程序的前一部分的若干程序段加载到内存
最终内存中放置了多个程序的多个程序段
这些程序段要根据优先级排队
CPU从队头获取下一个程序来运行
大量的程序段/进程,在频繁的奇幻CPU的时候会占用CPU的时间
做压栈和弹栈的工作,和需要部分内存存储栈中的数据
线程
因为并发会导致占用CPU的时间和内存,那么就把进程再次划分为多个更小的程序片段叫做线程
其实在CPU上来回切换的是线程,因为所有的线程共享进程的资源
在进程中包含的一个或多个线程是能够执行的独立单元
结论
CPU划片段
程序划进程段
进程段划线程段
真正在CPU上运行的是线程
尽量提高CPU的利用率
减少使用内存
进程还拥有一个私有的虚拟的内存地址,该空间仅能被他包含的线程访问,县城只能归属某一个进程,并且他只能访问该进程的所有的资源
当操作系统创建了一个进程后,该进程就会自动申请一个名为主线程或者是首要线程的线程
同类的多个线程共享一块内存空间和一组资源,县城北神有一个供程序执行的堆栈,
线程在CPU切换时负荷小,因此线程被称之为轻负荷进程,一个进程可以包含多个线程
进程和线程的区别:
一个进程至少有一个线程
线程的划分尺度小于进程,是的多个线程并发性高
另外,进程在指定的过程中,拥有独立的内存单元,而多个线程共享进程的内存空间,从而提高了程序的运行效率
线程在执行的过程中,与进程的区别在于每个独立的线程有一个程序的运行入口
顺序执行序列和程序出口,但线程不能独立运行,必须依存于进程,应用程序中由应用程序提供线程的控制
线程的应用场景:
线程通常用于一个应用程序中需要同时完成多个任务的情况
我们可以将每个任务定义一个线程,是的他们得以一同工作
也可以用于单一线程中完成,但是使用多线程可以更快的完成所需要的功能
线程的使用
在Java中能通过Java的api做线程编程操作
- 方案一:继承Thread类
Thread类:
此类是线程类.其每一个实例对象表示一个可以并发运行的线程
我们可以通过继承此类并重写run方法来定义一个具体的线程
在run方法中体现的是线程的功能(有线程的入口和出口)
启动线程时,调用线程对象的star方法,而不是直接调用run方法
start方法将线程纳入线程调度,使当前线程可以并发运行
当线程获取到CPU的时间片后,可以自动运行run方法中的逻辑代码
- 方案二:实现Runnable接口
Runnable接口
实现此接口并重写run方法来定义线程类,然后再创建线程段额时候
将Runnable接口的实力传入并启动线程
这样做的好处可以将线程和线程要执行的任务逻辑分离减少耦合
同时Java是单继承,定义一个实现Runnable接口
这样做可以更好的区实现其他的接口和继承父类
做线程的目的
做线程的目的实际上是为了做多线程,多线程的目的就是为了能并发
能并发的目的就是为了提高CPU的利用率
做多线程的两种方式:
- 多个线程共享一个run
- 多个线程可以每个线程一个run
- 前两种的组合也是多线程
Java程序员可以写线程,不需要程序员关心线程在哪个进程里
继承Thread类
public class MyThread extends Thread{
public void run(){
//线程的入口
//线程的任务逻辑代码
//线程的出口
}
}
使用:
MyThread mt=new MyThread();
mt.start(0;//调用重写的run方法,实际上是多态
实现Runnable接口
public class RunLuoJi implements Runnable{
public void run{
//方法体
}
}
使用:
Thread t=new Thread(new RunLuoJi);
t.start();//执行的是Thread中的方法
有Thread的run方法中调用接口的run方法
线程的状态
- 创建线程的对象 创建态 Thread t=new Thread();
- 创建完的线程对象 就绪态 t.start();//排到线程的队列中,不一定马上获取CPU
- 获取到CPU的资源 执行态 执行了run()方法,相当于线程出队列了
- 执行到某一个点时有 阻塞态 从CPU上下来;//io.sleep.wait等阻塞
- 如果正常执行完run() 结束态/消亡态 等待GC回收
线程的状态转化:
创建态-->就绪态
就绪态-->执行态
执行态-->阻塞态
-->就绪态
-->结束态/消亡态
阻塞态-->就绪态
结论:
- 根据业务,要考虑有多少个任务逻辑就写多少个run
- 每一个run要执行多少次
- 执行每一次run的时候,run是否使用数据,要考虑数据的安全性问题
用内部类创建线程对象
通常我们可以通过匿名内部类的方式创建线程
使用该方式可以简化编写代码的复杂度
当一个线程仅需要一个实例时
我们通常可以使用如下方式:
方式一:实现类是匿名的,对象有名
Thread t=new Thread(){
public void run(){
System.out.println("run方法");
}
};
t.start();
方式二:实现类是匿名的,对象也是匿名的
new Thread(){
public void run(){
System.out.println("run方法");
}
}.start();
方式三:实现类是匿名的,对象不是匿名的,有对象名
Runnable r=newRunnable(){
public void run(){
System.out.println("run方法");
}
};
Thread t=new Thread(r);
t.start();
方式四:对象时匿名的,实现类也是匿名的
new Thread(new Runnable(){
public void run(){
System.out.println("run方法");
}
}).start();
方式五:实现类是匿名的,对象是有名的
Thread t=new Thread(new Runnable(){
public void run(){
System.out.println("run方法");
}
});
t.start();
创建线程对象:
1. 继承自Thread类,重写run方法
优点:在当前的线程类中,可以回去run方法并重写,同时在当前线程类中也可以访问Thread类中的方法
缺点:当前线程类不能继承其他的类,Java是单继承
2. 实现Runnable接口,并重写run方法
优点:当前类可以多实现,还可以继承一次,可以吧线程对象和线程的任务逻辑代码分离出来
缺点:Runnable接口的实现类只有一个run方法,想要使用其他的线程方法需要
Thread t=new Thread(Runnable接口的实现类对象);
3. 特殊的用法,匿名内部类
优点:写法简单,编码量少
缺点:run的实现只有一次,且对象只有一个