进程线程
程序
- 就是为了完成特定的任务,用某种语言编写的一组指令的集合。简单的说就是我们写的代码
进程
- 进程是指运行中的程序,比如QQ,就启动了一个进程,操作系统就会为该进程分配内存空间。当我们使用其他程序的时候,就又启动了一个进程,操作系统就会为其他进程分配新的内存空间
- 进程是程序的一次执行过程,或者是正在运行的一个程序。
- 是一个动态的过程,有他自身的产生、存在和消亡的过程
下面就是几个进程
线程
- 线程是由进程创建的,是进程的一个实体
- 一个进程可以拥有多个线程,如下图【同一个应用迅雷 下载多个内容,就产生多个线程】
多线程:
同一时刻,可以执行多个进程,比如上面的迅雷同时下载多个文件
单线程:
同一时刻,只允许执行一个线程
并发 :
同一时刻 , 多个任务交替执行, 造成一种‘貌似同时’ 的错觉, 简单的说,单核cpu实现的多任务就是并发
看上去他们同时进行,但是在任意时间段内,他们都是相互交错执行的
比如 : ‘人的大脑一边说话,一边写字 ’
并行 :
同一时刻, 多个任务同时执行。多核cpu可以实现并行
线程使用的两种方式
- 当一个类继承Thread类, 那么该类就可以当成一个线程
线程常用的方法
两种方式的区别
- 从java的设计来看, 通过继承Thread或者实现Runnable接口来创建线程本质上是没有区别的,他们都是通过start方法来调用start0 ,然后实现了多线程
- 从jdk帮助文档来看, Thread类本身就实现了Runnable接口
- 实现Runnable接口方式更加适合多线程共享一个资源的情况,并且避免了单继承的限制
一、 继承Thread创建线程
案例一: 开启一个线程,该线程每隔1秒,在控制台输出一段话
public class Thread01 { public static void main(String[] args) { //1. 创建一个cat对象,可以当作一个线程来使用 Cat cat = new Cat(); cat.start(); //启动线程 - > 最终会执行cat的run方法 } } class Cat extends Thread{ int times = 0; //todo 通过重写run方法,来实现自己的业务逻辑 @Override public void run() { // 实现了Runnable接口的run方法 while(true){ System.out.println("执行一次" + "次数" +(++times)); //让其休眠一秒 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
结果就是每隔一秒输出一次
二、 实现Runnable
- java是单继承的,在某些情况下一个类可能已经继承了某个父类,这是再用Thread类方法来创建线程显然是不可能了
- java设计者们就想出了另一个创建线程的方法,就是通过实现Runnable接口来创建线程
实现案例:
写一个程序,该程序可以每隔1秒,在控制台输出一段话
public class Thread02 { public static void main(String[] args) { Dog dog = new Dog(); //无法调用start方法 ,切记不能调用run方法,因为他只是一个普通的方法,没办法开启线程 Thread thread = new Thread(dog); //这时 ,我们可以创建一个Thread对象, 然后调用start方法 ,然后就可以启动一个线程,调用了run方法 thread.start(); } } class Dog implements Runnable{//通过Runnable接口实现线程 int times = 0; @Override public void run() { System.out.println("执行一次" + "次数" +(++times)+ "线程名:" + Thread.currentThread().getName()); //让其休眠一秒 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }
这里使用的是一个设计模式【代理模式】
多线程机制
当我们执行上述程序是,它的流程是这样的
主线程的执行
public static void main(String[] args) throws Exception{ int count = 0; //1. 创建一个cat对象,可以当作一个线程来使用 Cat cat = new Cat(); cat.start(); //当main方法执行一个线程 之后 ,主线程不会阻塞 ,会继续执行 while (true){ System.out.println("主线程 执行次数: " + (++count) + "线程名:" + Thread.currentThread().getName()); Thread.sleep(1000); } }
当main方法执行一个线程 之后 ,主线程不会阻塞 ,会继续执行
最后用检测工具发现,虽然主线程的执行完一定次数之后,结束了,但是它的子线程Thread-0 没有执行完 ,那么程序就不会结束
为什么这里要调cat.start()方法?
答 :因为run方法就是一个普通的方法,并无法启动一个真正的线程就会产生阻塞。
- 源码分析start方法
public synchronized void start() { if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { } } } //本地方法, 是由jvm调用,底层是由c/c++实现,无法调用 private native void start0();
换言之, 真正实现多线程的效果是start0();方法
多个子线程案例
public class Thread02 { public static void main(String[] args) { Dog dog = new Dog(); Catt catt = new Catt(); //无法调用start方法 ,切记不能调用run方法,因为他只是一个普通的方法,没办法开启线程 Thread thread1 = new Thread(dog); Thread thread2 = new Thread(catt); //这时 ,我们可以创建一个Thread对象, 然后调用start方法 ,然后就可以启动一个线程,调用了run方法 thread1.start(); thread2.start(); } } class Dog implements Runnable{//通过Runnable接口实现线程 int times = 0; @Override public void run() { while (true){ System.out.println("执行一次" + "次数" +(++times)+ " 线程1名:" + Thread.currentThread().getName()); //让其休眠一秒 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Catt implements Runnable{//通过Runnable接口实现线程 int times = 0; @Override public void run() { while (true){ System.out.println("执行一次" + "次数" +(++times)+ " 线程2 名:" + Thread.currentThread().getName()); //让其休眠一秒 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
运行结果 ,他们交叉执行