小玉这几天在复习多线程篇知识,最近有点偷懒了,博客要常常写!加油! 那么接下来就跟着小玉来入门多线程吧.....
目录
1.什么是进程?
2.什么是线程?
2.1进程&线程的区别是什么?
3.创建多线程的几种方法
3.1方法一:继承Thread类
3.2方法二:实现Runnable接口
3.3方法三:采用匿名内部类
3.3.1继承Thread,采用匿名内部类
3.3.2实现Runnable,采用匿名内部类
3.4方法四:采用Lambda表达式 (最推荐使用)
4.Thread常见构造方法及属性
在操作系统没有引入进程之前,由于CPU一个核心一次只能执行一个程序,所以多个程序只能顺序执行,像以前的手机QQ退出后就无法接受消息了.....CPU的速度很快,磁盘、网路等IO的速度很慢,造成CPU会有大量空闲的时间,此时CPU的利用率很低,为了解决CPU的利用率低的问题,操作系统引入了进程以及中断处理,实现了在同一时间段内,多个程序的"并发"执行,这个程序执行一点,那个程序执行一点,这样并发交替的执行大大提高了CPU的利用率。
简单来说:跑起来的程序就是进程!一个.exe文件就是一个进程,例如QQ,微信,CCTALK......
一个线程就是一个 "执行流". 每个线程之间都可以按照顺序执行自己的代码. 多个线程之间 "同时" 执行着多份代码.就例如QQ:QQ是进程,但是QQ可以同时兼顾聊天视频,看朋友圈,发图片等等功能,这些功能可以同时执行,这即是线程.
线程很好的解决了"CPU资源浪费的情况",可以在CPU快速调度的时候,充分减少等待时间,提高计算机的使用效率.现如今,"并发编程"已经成为主流,学习多线程也是刚需!
很多朋友在了解完进程和线程这两概念之后就陷入迷茫,我们下面就来探索一下他们之间的区别究竟在哪........
- 进程包含线程
好,第一点盖棺定论!一个包含关系直击痛处! 一个进程至少包含一个线程,也可以包含多个线程,线程不可脱离进程存在.那么既然包含关系存在那么我们能得出有效结论:
- 线程是独立的执行流,同一个进程的多个线程之间共享同一份资源
OK! 我们如果将进程比喻成一个"工厂",那么线程就是工厂的"流水线",他们之间可以并发执行.那么"共享资源"到底在说什么? 资源----即内存空间和文件描述附表.
只有这个进程的第一个线程创建的时候才会去申请资源,这些资源申请完之后,后续在该进程里创建新的线程之后就无须再申请了.
- 线程是操作系统资源调度的最小单位,进程是操作系统资源分配的最小单位.
有了前面的结论铺垫我们也能得出这个结论,我们之前说的进程也可以进行调度,这里指的是这个进程只有一个线程的时候,操作系统调度的其实是这个进程的线程,所以我们才会以为进程在频调度......
- 进程具有独立性和隔离性.一个进程挂了不影响其他进程,但是一个线程挂了可能会吧进程和进程里的其他线程都带走
就比如QQ哪天挂了,但是微信不受影响,但是要是QQ发不了消息了,可能就进入死机状态,看朋友圈打视频可能都干不了了..........
好,以上就是进程&线程的区别,大家可以仔细体会一下,这里很好理解,希望去哪个大家不要想的太多,复杂化了........
那么我们了解完了进程&线程,在Java中我们重点学习多线程,那么如何创建一个多线程呢?我们打开IDEA:像往常一样,建一个普通的文件:
在类外创建一个类并且继承Thread类,重写run();这里实际上是针对原本的Thread库实现扩展,在里面我们打印上一句"hello t ";在下面public类里我们创建子类实例t,并且调用t.start() 创建并启动新线程!
上述代码中我们涉及到的进程是Java进程,其中包含两个线程:主线程main 和我们创建的线程t.接下来我们再感受一个什么叫:每个线程都是独立的执行流.
我们设置两个死循环,如果是以前的思维:那么程序进入main之后,遇到t.start(),会在这个循环里出不来,那样就不可能执行到下面的第二个死循环了,但是我们观察控制台:
我们可以发现:这里能交替打印出 hello t 和 hello main 如果想更清楚一点,我们可以设置休眠:
此时在观察控制台:
我们就能更清楚的看到交替的过程了,关于为什么有时候先打印hello t有时候先打印hello main,这里小玉想说:系统的调度是无序/随机的,虽然我们各自设置了休眠,但是我们无法控制哪个线程先执行. 这里就能体现那句话了,什么来着?每个线程都是独立的执行流.
那么介绍完了第一个方法,还有的就是:实现Runnable接口,看代码:
class MyThread2 implements Runnable{
@Override
public void run() {
while (true) {
System.out.println("hello t");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadDemo2 {
public static void main(String[] args) {
MyThread2 myThread2 = new MyThread2();
Thread t = new Thread(myThread2);
t.start();
while (true){
System.out.println("hello main ");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
结果跟方法一是一样的,这里就不放了,朋友们自行实践一下吧~~~
这里需要注意! 重写的是run()方法,但是我们启动新线程却是调用start()方法,可否启动新线程直接调用run()呢?
run() 与start() 的区别:
- run()方法可以视作新线程的入口方法,就类似于主线程的main()方法一样,在我们调用start()方法是会自动调用run()方法,因为这是我们重写的方法.
- run()方法不会创建新线程,只是线程的入口指示牌,但是start()方法会创建一个新线程!
我们来看例子吧:
class MyThread2 implements Runnable{
@Override
public void run() {
while (true) {
System.out.println("hello t");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadDemo2 {
public static void main(String[] args) {
MyThread2 myThread2 = new MyThread2();
Thread t = new Thread(myThread2);
// t.start();
t.run();
while (true){
System.out.println("hello main ");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
只需要改动一行代码,我们观察控制台:
我们发现,hello main不打印了,为什么?看图解:
此时我们的线程只有主线程main,main进入第一个死循环之后就出不去了,只能一秒一个hello t......
你们理解什么是:线程是独立的执行流这句话了吗?
所谓的匿名内部类就是定义在类里面的类,过两天小玉深度学习一下,写一篇博客吧....小玉也不是很懂......
public class ThreadDemo3 {
public static void main(String[] args) {
Thread t = new Thread(){
@Override
public void run() {
while (true){
System.out.println("hello t");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t.start();
while (true){
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果同方法一
public class ThreadDemo4{
public static void main(String[] args) {
Thread t = new Thread(new Runnable(){
@Override
public void run() {
while (true){
System.out.println("hello t");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
while (true){
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
注意了这里不需要implements Runnable直接在括号里new Runnable即可;效果相同~~~
大括号{放在哪里就是针对哪个类的内部类,大家区分清楚.
Lambda 表达式实际上就是一个匿名函数,本质上就是一个连名字都不配拥有的函数,用完就丢,一次性.
public class ThreadDemo5 {
public static void main(String[] args) {
Thread t = new Thread(()->{
while (true){
System.out.println("hello t");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
while (true){
System.out.println("hello main ");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
我们发现这种方法连run()都不需要写了......很方便,但是大家注意格式:
构造方法:
属性:
关于是否是后台线程,这里贤臣共分为前台和后台线程,前台线程会阻止线程结束,当前台线程结束的时候,整个线程才能结束,这个isDaemon()方法默认是false,也就是默认是前台线程,了解即可.......
小玉就说这么多吧......更多精彩内容小玉会努力更新的,欢迎留言讨论!再见了~~~~