1. 程序 进程 线程
程序
(program)
是为完成特定任务、用某种语言编写的一组指令的集合。即指
一 段静态的代码
,静态对象。
进程
(process)
是程序的一次执行过程,或是
正在运行的一个程序
。是一个动态 的过程:有它自身的产生、存在和消亡的过程。——
生命周期
程序是静态的,进程是动态的
进程作为资源分配的单位,
系统在运行时会为每个进程分配不同的内存区域
线程
(thread)
,进程可进一步细化为线程,是一个程序内部的一条执行路径。
若一个进程同一时间
并行
执行多个线程,就是支持多线程的
线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器
(pc)
,线程切换的开销小
一个进程中的多个线程共享相同的内存单元
/
内存地址空间
它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来
安全的隐患
。
例如下面每打开一个软件就相当于开了一个进程
2. 单核 多核 并行 并发
单核
CPU
和多核
CPU
的理解
单核
CPU
,其实是一种假的多线程,因为在一个时间单元内,也只能执行一个线程的任务。例如:虽然有多车道,但是收费站只有一个工作人员在收费,只有收了费才能通过,那么CPU
就好比收费人员。如果有某个人不想交钱,那么收费人员可以把他“挂起”(晾着他,等他想通了,准备好钱,再去收费)。但是因为CPU
时 间单元特别短,因此感觉不出来。
如果是多核的话,才能更好的发挥多线程的效率。(现在的服务器都是多核的)
一个
Java
应用程序
java.exe
,其实至少有三个线程:
main()
主线程,
gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。
并行与并发
并行:
多个
CPU
同时执行多个任务。比如:多个人同时做不同的事。
并发:
一个
CPU(
采用时间片
)
同时执行多个任务。比如:秒杀、多个人做同一件事。
使用多线程的优点
背景:
以单核
CPU
为例,只使用单个线程先后完成多个任务,肯定比用多个线程来完成用的时间更短,因为单核CPU在开多个线程的时候,为了使这两个线程同时工作,CPU需要很快地不停地切换所处理的线程(单核CPU也可以开多线程),切换也需要时间。但是如果是多核CPU就可以不同的核处理不同的线程。
为何仍需多线程呢?
多线程程序的优点:
1.
提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
2.
提高计算机系统
CPU
的利用率
3.
改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改
何时需要多线程
程序需要同时执行两个或多个任务。
程序需要实现一些需要等待的任务时,比如说你在看微博的时候,当网络比较慢,下载图片和下载文字如果是同一个线程,那么必须等图片加载完了才可以去加载后面的文字,但是如果是不同的线程加载,就会先加载出存储比较小的文字,图片慢慢加载
需要一些后台运行的程序时
3. Thread类:创建多线程
Java
语言的
JVM
允许程序运行多个线程,它通过
java.lang.Thread
类来体现。
Thread类的特性
每个线程都是通过某个特定
Thread
对象的
run()
方法来完成操作的,经常
把
run()
方法的主体称为
线程体
通过该
Thread
对象的
start()
方法来
启动这个线程
,而非直接调用
run()
- Thread类实现了Runnable()接口
方法一:用继承Thread类的方式
package thread;
/* 多线程的创建
* 方式一:继承与Thread类
*
* 1.创建一个继承于Thread类的子类
* 2.重写Thread类的run()方法,将此线程要执行的方法写在run的方法体中
* 3.创建Thread的子类的对象
* 4.通过子类调用start()方法
*/
class thread extends Thread{
@Override
public void run() {
for(int i=0;i<=10;i++){
if(i%2==0){
System.out.println(i);
}
}
}
}
class threadTest{
public static void main(String[] args){
//main方法内就是一个线程(主线程)
thread t1 = new thread(); //注意这一条语句仍然是在主线程中执行的
t1.start(); //从这里开始,又多了一个新的线程,也就是说这个线程执行run方法体的代码,与main()中下面的for循环同时执行。
for(int i=0;i<=10;i++){
if(i%2!=0){
System.out.println(i+"主线程");
}
}
}
}
从程序结果可以看出来,两个线程同时执行。
注意:
- 一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上的异常“IllegalThreadStateException,需要重新造一个对象来调用start方法。
方法二:
实现
Runnable
接口的方式
1)
定义子类,实现
Runnable
接口。
2)
子类中重写
Runnable
接口中的
run
方法。
3)
通过
Thread
类含参构造器创建线程对象。
4)
将
Runnable
接口的子类对象作为实际参数传递给
Thread
类的构造器中。
5)
调用
Thread
类的
start
方法:开启线程,调用
Runnable
子类接口的
run
方法。
package thread;
class myThread3 implements Runnable{
//实现类myThread3实现接口Runnable。(通过重写run方法)
@Override
public void run() {
for(int i=0;i<=10;i++){
System.out.println(Thread.currentThread().getName()+"**"+i);
}
}
}
public class thread2{
public static void main(String[] args){
//创建实现类myThread3的对象T
myThread3 T = new myThread3();
//将T作为参数传递到Thread的构造器中创建Thread的对象
Thread t1 = new Thread(T);
t1.setName("线程一");
t1.start();
Thread t2 = new Thread(T);
t2.setName("线程二");
t2.start();
}
}
注意:
- 上面程序里Thread t1 = new Thread(T); 和Thread t2 = new Thread(T); 传入的实现类对象T是同一个
- Runnable接口只有一个抽象方法:run()。
- Thread类定义了一个含参构造器,Thread(Runnable target)。
- Thread t1 = new Thread(T); 实际传入的是Runnable的实现类,体现了多态性。
- 然后再调用t1.start()时候,run方法里的线程就开始跑了。
4. 例题:
- 分别利用两种多线程的方式,实现三个窗口同时卖票
1.继承方式
package thread;
class window extends Thread{
private static int ticket = 100;
@Override
public void run() {
while (true){
if(ticket>0){
System.out.println(currentThread().getName()+"票号为:"+ticket);
ticket--;
}else {
System.out.println("没票啦");
break;
}
}
}
}
public class test{
public static void main(String[] args){
window w1 = new window();
window w2 = new window();
window w3 = new window();
w1.start();
w2.start();
w3.start();
}
}
注意这里的ticket要定义成static才能让三个线程同时操作同一个ticket。
2.实现接口的方式
package thread;
class window implements Runnable{
private int ticket = 10;
@Override
public void run() {
while (true){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"票号为:"+ticket);
ticket--;
}else {
System.out.println("没票啦");
break;
}
}
}
}
public class test{
public static void main(String[] args){
window T = new window();
Thread t1 = new Thread(T);
Thread t2 = new Thread(T);
t1.start();
t2.start();
}
}
注意这里的ticket就不是static,因为多个线程在创建时都是传入的window的T对象,各个线程共享window的T对象中的数据。
第二个卖票程序的输出: 注意这里有两个窗口都卖了第10个票,出现了线程安全问题。线程安全以后再说
5. 比较两种创建多线程的方式
* 比较创建线程的两种方式
* 开发中:优先选择:实现Runnable接口的方式
* 原因:1. 实现的方式没有类的单继承性的局限性,如果用继承的方式,window继承Thread后就不能有其他的父类了。
* 2. 实现的方式更适合来处理多个线程有共享数据的情况。
* 联系:public class Thread implements Runnable
* 相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。