小玉这几天在复习多线程篇知识,最近有点偷懒了,博客要常常写!加油!  那么接下来就跟着小玉来入门多线程吧.....

目录

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常见构造方法及属性 

         

1.什么是进程?

        在操作系统没有引入进程之前,由于CPU一个核心一次只能执行一个程序,所以多个程序只能顺序执行,像以前的手机QQ退出后就无法接受消息了.....CPU的速度很快,磁盘、网路等IO的速度很慢,造成CPU会有大量空闲的时间,此时CPU的利用率很低,为了解决CPU的利用率低的问题,操作系统引入了进程以及中断处理,实现了在同一时间段内,多个程序的"并发"执行,这个程序执行一点,那个程序执行一点,这样并发交替的执行大大提高了CPU的利用率。         简单来说:跑起来的程序就是进程!一个.exe文件就是一个进程,例如QQ,微信,CCTALK......

2.什么是线程?

        一个线程就是一个 "执行流". 每个线程之间都可以按照顺序执行自己的代码. 多个线程之间 "同时" 执行着多份代码.就例如QQ:QQ是进程,但是QQ可以同时兼顾聊天视频,看朋友圈,发图片等等功能,这些功能可以同时执行,这即是线程. 

        线程很好的解决了"CPU资源浪费的情况",可以在CPU快速调度的时候,充分减少等待时间,提高计算机的使用效率.现如今,"并发编程"已经成为主流,学习多线程也是刚需!

2.1进程&线程的区别是什么?

        很多朋友在了解完进程和线程这两概念之后就陷入迷茫,我们下面就来探索一下他们之间的区别究竟在哪........

进程包含线程

        好,第一点盖棺定论!一个包含关系直击痛处!  一个进程至少包含一个线程,也可以包含多个线程,线程不可脱离进程存在.那么既然包含关系存在那么我们能得出有效结论:

创建线程比创建进程更快.

销毁线程比销毁进程更快.

调度线程比调度进程更快.

(即:线程比进程更轻量~~~)

线程是独立的执行流,同一个进程的多个线程之间共享同一份资源

        OK! 我们如果将进程比喻成一个"工厂",那么线程就是工厂的"流水线",他们之间可以并发执行.那么"共享资源"到底在说什么? 资源----即内存空间和文件描述附表. 

        只有这个进程的第一个线程创建的时候才会去申请资源,这些资源申请完之后,后续在该进程里创建新的线程之后就无须再申请了.

线程是操作系统资源调度的最小单位,进程是操作系统资源分配的最小单位. 

        有了前面的结论铺垫我们也能得出这个结论,我们之前说的进程也可以进行调度,这里指的是这个进程只有一个线程的时候,操作系统调度的其实是这个进程的线程,所以我们才会以为进程在频调度...... 

进程具有独立性和隔离性.一个进程挂了不影响其他进程,但是一个线程挂了可能会吧进程和进程里的其他线程都带走

        就比如QQ哪天挂了,但是微信不受影响,但是要是QQ发不了消息了,可能就进入死机状态,看朋友圈打视频可能都干不了了..........

        好,以上就是进程&线程的区别,大家可以仔细体会一下,这里很好理解,希望去哪个大家不要想的太多,复杂化了........

3.创建多线程的几种方法

        那么我们了解完了进程&线程,在Java中我们重点学习多线程,那么如何创建一个多线程呢?我们打开IDEA:像往常一样,建一个普通的文件:

3.1方法一:继承Thread类

        在类外创建一个类并且继承Thread类,重写run();这里实际上是针对原本的Thread库实现扩展,在里面我们打印上一句"hello t ";在下面public类里我们创建子类实例t,并且调用t.start() 创建并启动新线程!

        上述代码中我们涉及到的进程是Java进程,其中包含两个线程:主线程main 和我们创建的线程t.接下来我们再感受一个什么叫:每个线程都是独立的执行流.

         我们设置两个死循环,如果是以前的思维:那么程序进入main之后,遇到t.start(),会在这个循环里出不来,那样就不可能执行到下面的第二个死循环了,但是我们观察控制台:

        我们可以发现:这里能交替打印出 hello t 和 hello main 如果想更清楚一点,我们可以设置休眠: 

此时在观察控制台: 

        我们就能更清楚的看到交替的过程了,关于为什么有时候先打印hello t有时候先打印hello main,这里小玉想说:系统的调度是无序/随机的,虽然我们各自设置了休眠,但是我们无法控制哪个线程先执行. 这里就能体现那句话了,什么来着?每个线程都是独立的执行流.

3.2方法二:实现Runnable接口 

        那么介绍完了第一个方法,还有的就是:实现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...... 

你们理解什么是:线程是独立的执行流这句话了吗? 

3.3方法三:采用匿名内部类 

        所谓的匿名内部类就是定义在类里面的类,过两天小玉深度学习一下,写一篇博客吧....小玉也不是很懂...... 

3.3.1继承Thread,采用匿名内部类 

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();

}

}

}

}

运行结果同方法一 

3.3.2实现Runnable,采用匿名内部类

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即可;效果相同~~~ 

大括号{放在哪里就是针对哪个类的内部类,大家区分清楚.

3.4方法四:采用Lambda表达式 (最推荐使用)

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()都不需要写了......很方便,但是大家注意格式: 

4.Thread常见构造方法及属性 

构造方法:

属性:

关于是否是后台线程,这里贤臣共分为前台和后台线程,前台线程会阻止线程结束,当前台线程结束的时候,整个线程才能结束,这个isDaemon()方法默认是false,也就是默认是前台线程,了解即可.......

        小玉就说这么多吧......更多精彩内容小玉会努力更新的,欢迎留言讨论!再见了~~~~ 

        

         

精彩内容

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: