1. **一、线程概述**

    进程:一个在内存中运行的应用程序。
    线程:进程上的一个执行单元,一个进程可以有多个线程。

    多进程:在操作系统中同时运行多个程序。
    多线程:在同一应用程序中有多个执行单元同时执行

    线程的生命周期:—个线程从创建到执行完的整个过程
    多线程能解决什么问题:多线程能并发执行程序,提高程序的运行效率。

    jvm就是一个进程:

    守护线程(垃圾回收),主线程(main函数)

    二、如何创建线程对象:

    第一种方法:继承Thread类:创建一个线程的子类去继承线程类,因为线程类没有实现类,无法实现功能。同时子类需要重写run()方法。想要线程跑起来,代码必须写在run()方法中。
    开启线程用start()方法。
    在一个线程中开启另外一个新线程,则新开线程称为该线程的子线程,子线程初始优先级与父线程相同。不过主线程先启动占用了cpu资源,因此主线程总是优于子线程。如下面的main线程与thread线程

    1. public class ThreadDemo01 {
    2. public static void main(String[] args) {
    3. //创建线程对象,新建状态
    4. ThreadImpl thread = new ThreadImpl();//新建状态
    5. //开启线程,会让线程进入就绪状态
    6. //就绪状态:拥有争夺CPU时间片的权利
    7. thread.start();
    8. //把遍历操作放一份在主线程
    9. for (int i = 0;i<100;i++){
    10. System.out.println("主线程"+i);
    11. }
    12. }
    13. }
    14. /**
    15. * 子类继承Thread后run()必须重写
    16. */
    17. class ThreadImpl extends Thread{
    18. @Override
    19. public void run() {//执行到run方法 多线程处于运行状态
    20. for (int i = 0;i<100;i++){
    21. //获取当前线程的线程名称
    22. System.out.println(currentThread().getName()+"--"+i);
    23. }
    24. }
    25. }

    image.png

    1. package com.jy.Thread;
    2. public class ThreadDemo02 {
    3. public static void main(String[] args) {
    4. CreateThread createThread1 = new CreateThread();
    5. //设置线程名称
    6. createThread1.setName("分支线程-1");
    7. CreateThread createThread2 = new CreateThread();
    8. createThread2.setName("分支线程-2");
    9. CreateThread createThread3 = new CreateThread();
    10. createThread3.setName("分支线程-3");
    11. createThread1.start();
    12. createThread2.start();
    13. createThread3.start();
    14. }
    15. }
    16. class CreateThread extends Thread{
    17. @Override
    18. public void run() {
    19. for (int i = 0;i<100;i++){
    20. //返回的是当前线程的对象
    21. Thread thread = Thread.currentThread();
    22. //获取当前线程的线程名称
    23. System.out.println(thread.getName());
    24. }
    25. }
    26. }

    ps:使用睡眠可以使进程进入阻塞状态,阻塞状态会释放cpu时间片
    如:Thread.sleep(100);
    多线程中主线程与子线程执行的顺序:https://www.yuque.com/wenbusheng-5qk2j/is5soq/qb50a7/edit

    第二种方法:实现Runnable接口

    1. /**
    2. * 创建线程的第二种方式,实现Runnable接口
    3. * 实现接口Runnable的方式来创建线程对象,但要注意所创建的对象new CreateThread01()本身不是线程对象
    4. * 创建线程对象:Thread thread1 = new Thread(new CreateThread01());
    5. */
    6. public class ThreadDemo03 {
    7. public static void main(String[] args) {
    8. //创建线程对象
    9. Thread thread1 = new Thread(new CreateThread01());
    10. Thread thread2 = new Thread(new CreateThread01());
    11. Thread thread3 = new Thread(new CreateThread01());
    12. thread1.start();
    13. thread2.start();
    14. thread3.start();
    15. }
    16. }
    17. //虽然实现了Runnable接口,但并不是线程类,只是一个实现类,因为Runnable不是线程接口
    18. class CreateThread01 implements Runnable{
    19. @Override
    20. public void run() {
    21. }
    22. }

    第三种方法:通过匿名内部类重写runnable里的run方法的方式创建线程对象

    1. /**
    2. * 通过匿名内部类的方式创建线程对象
    3. */
    4. public class ThreadDemo04 {
    5. public static void main(String[] args) {
    6. //创建线程对象
    7. Thread thread = new Thread(new Runnable() {
    8. @Override
    9. public void run() {
    10. }
    11. });
    12. thread.start();
    13. }
    14. }

    第四种方法 使用Callable结合Future实现多线程编程。(很重要)
    可以获得返回值,前两种没有返回值。

    实现方式: **FutureTask futureTask = new FutureTask(new Callable())


    FutureTask是继承与Future的**
    缺点:在获取线程返回值之前,可以能造成主线程阻塞

    1. package com.jy.Thread;
    2. import java.util.concurrent.Callable;
    3. import java.util.concurrent.ExecutionException;
    4. import java.util.concurrent.FutureTask;
    5. public class ThreadDemo08 {
    6. public static void main(String[] args) {
    7. //创建一个任务类对象futureTask 不是线程对象
    8. FutureTask futureTask = new FutureTask(new Callable() {
    9. @Override
    10. public Object call() throws Exception {
    11. int num = 0;
    12. for (int i = 0; i < 10; i++) {
    13. System.out.println(Thread.currentThread().getName()+" "+i);
    14. num+=i;
    15. }
    16. return num;
    17. }
    18. });
    19. //创建线程对象
    20. Thread thread = new Thread(futureTask);
    21. thread.setName("t1");
    22. thread.start();
    23. //获取线程的返回值
    24. try {
    25. Object o = futureTask.get();
    26. System.out.println("t1线程的返回值结果为"+o);
    27. } catch (InterruptedException e) {
    28. e.printStackTrace();
    29. } catch (ExecutionException e) {
    30. e.printStackTrace();
    31. }
    32. System.out.println("主线程结束");
    33. }
    34. }

    上述代码中,在获得返回值num后,才会输出“主线程结束”,即主线程被阻塞。

    三、线程的生命周期

    1、新建状态:线程对象被创建

    2、就绪状态:线程对象调用start方法后就会处于就绪状态,拥有争夺cpu时间片的权利

    3、运行状态:当某一个线程争夺到cpu使用权后,就会执行run方法,执行run方法时就处于运行状态

    4、阻塞状态:当某一个线程处于运行态时,发生了睡眠sleep或者控制台打印等需要等待的操作,这时线程就会进入阻塞状态,释放cpu使用权

    5、死亡状态:当run方法执行结束后,即死亡状态。

    **四、线程在jvm内存上的分布

    **
    1、每一个线程对应一个栈(如主线程main占用一个栈)
    2、栈的资源是不共享的

    3、堆的资源是共享的,因为new出的对象都在堆中开辟空间,堆只有一个。
    Java学习二十——多线程(一) - 图2
    **五、线程调度模型

    **
    抢占式调度模型,优先级高的线程抢到cpu时间片的概率更高。Java就是抢占式。

    均分式调度模型,平均分配时间片。
    Java中线程优先级默认为5.所有线程争夺CPU的概率相同。
    getPriority()方法,获得线程优先级。

    **六、yield() 让位方法

    **
    暂停当前正在执行的线程对象,并执行其他对象

    yield()方法的执行会让当前线程从运行状态回到就绪状态。
    使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。
    但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。