0.前言

  1. 并发:同一时刻,多个任务交替执行,单核多任务就是并发
  2. 并行:同一时刻,多个任务同时执行,多核cpu可以实现并行

    线程的7大状态

    image.png

    1.线程使用

    基础阶段创建线程两种方式
    image.png

    1.继承Thread

  3. 继承Thread类,重写run方法即可,在run方法中编写线程逻辑,创建对象后以start()启动线程,而start()中调用了start0(),start0()是本地方法,由jvm调用 private native void start0();

  4. 此方法创建的线程不能使用this作为锁,因为是new创建的对象,this是不同的

image.png

  1. package review.thread;
  2. /**
  3. * Thread方式创建线程
  4. * @Author rainbow
  5. * @Date 2022/5/15 18:54
  6. * @Version 1.0
  7. **/
  8. public class ThreadTest extends Thread {
  9. @Override
  10. public void run() {
  11. for (int i = 0; i < 10; i++) {
  12. System.out.println(Thread.currentThread().getName());
  13. try {
  14. Thread.sleep(100);
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. }
  18. }
  19. }
  20. public static void main(String[] args) {
  21. ThreadTest threadTest = new ThreadTest();
  22. ThreadTest threadTest1 = new ThreadTest();
  23. //start不是让线程立即执行,而是成为就绪状态,等待被cpu调用,不会阻塞后面的代码执行
  24. threadTest.start();
  25. threadTest1.start();
  26. }
  27. }

2.实现Runnable

  1. 当类已经继承了其他类却需要多线程时,可以用实现接口的方法,并通过将线程对象传给thread构造器创建thread对象,再调用start()(静态代理模式)
  2. 适合多个线程共享数据时使用,优先推荐此方式实现多线程
  3. 可以使用同一个对象创建多个线程 ```java package review.thread;

/**

  • Runnable创建线程
  • @Author rainbow
  • @Date 2022/5/15 19:29
  • @Version 1.0 **/ public class RunnableTest implements Runnable{ @Override public void run() {

    1. System.out.println("runnable创建的线程");

    }

    public static void main(String[] args) {

    1. //创建线程对象,但是没有start方法,不能直接调用
    2. RunnableTest runnableTest = new RunnableTest();
    3. //作为构造器参数创建thread对象,再调用start()
    4. Thread thread = new Thread(runnableTest);
    5. Thread thread1 = new Thread(runnableTest);
    6. thread.start();
    7. thread1.start();

    } }

  1. <a name="TzKBk"></a>
  2. ## 3.实现Callble
  3. callable比Runnable的功能更强大:
  4. 1. **call()可以有返回值(**futureTask.get()**)**
  5. 1. **方法可以抛出异常**
  6. 1. **支持泛型的返回值**
  7. 1. 需要借助**FutureTask**类,比如获取返回结果
  8. ```java
  9. package threadExample;
  10. import java.util.concurrent.Callable;
  11. import java.util.concurrent.ExecutionException;
  12. import java.util.concurrent.FutureTask;
  13. //使用callable创建多线程
  14. /*
  15. * callable比Runnable的功能更强大:
  16. * call()可以有返回值
  17. * 方法可以抛出异常
  18. * 支持泛型的返回值
  19. * 需要借助FutureTask类,比如获取返回结果
  20. * */
  21. //1.创建callable的实现类
  22. class NumThread implements Callable{
  23. //2.实现call方法,类似run
  24. @Override
  25. public Object call() throws Exception {
  26. int sum=0;
  27. for (int i = 0; i < 100; i++) {
  28. if (i % 2 != 0) {
  29. System.out.println(Thread.currentThread().getName() + ":\t" + i);
  30. sum+=i;
  31. }
  32. }
  33. return sum;
  34. }
  35. }
  36. public class CallableTest {
  37. public static void main(String[] args) {
  38. //3.创建callable实现类对象
  39. NumThread numThread = new NumThread();
  40. //4.创建futureTask对象
  41. FutureTask futureTask = new FutureTask(numThread);
  42. //5.传给thread,并调用start()
  43. new Thread(futureTask).start();
  44. try {
  45. //返回值即为FutureTask构造器参数Callable实现的call方法返回值
  46. Object sum= futureTask.get();
  47. System.out.println(sum);
  48. } catch (InterruptedException e) {
  49. e.printStackTrace();
  50. } catch (ExecutionException e) {
  51. e.printStackTrace();
  52. }
  53. }
  54. }

4.线程池

:::danger 提前创建好多个线程,放入线程池中,使用时直接获取,使用完 放回池中。可以避免频繁创建销毁、实现重复利用。
:::

  1. 提高响应速度(减少了创建新线程的时间)
  2. 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
  3. 便于线程管理(需要转实现类ThreadPoolExecutor)
    1. corePoolSize:核心池的大小
    2. maximumPoolSize:最大线程数
    3. keepAliveTime:线程没有任务时最多保持多长时间后会终止
      ```java package threadExample;

import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor;

class number implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + “:\t” + i); } } } }

class number1 implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 != 0) { System.out.println(Thread.currentThread().getName() + “:\t” + i); } } } }

//使用线程池的方法创建现程 public class ThreadTest { public static void main(String[] args) { //创建10个线程的线程池 ExecutorService service = Executors.newFixedThreadPool(10); //转类型是因为接口中没有配置属性,要设置就需要转类型 ThreadPoolExecutor service1 = (ThreadPoolExecutor) service; //设置最大线程数 service1.setMaximumPoolSize(10);

  1. //执行指定线程,提供一个实现runnable或callable接口的对象(submit())
  2. service1.execute(new number());//适合runnable
  3. service1.execute(new number1());//适合runnable

// service1.submit() 适合callable //关闭线程池 service1.shutdown(); }

}

  1. <a name="NxXrK"></a>
  2. # 2.线程常用方法
  3. 1. setName():设置线程名
  4. 1. getName():获得线程名
  5. 1. start():使线程就绪
  6. 1. setPriority():设置线程优先级
  7. 1. getPriority():获得线程优先级
  8. 1. **sleep()**:使线程休眠,但是**不会释放锁**
  9. 1. interrupt():中断线程
  10. 1. **yield()**:让出cpu给其他线程,但是时间不确定,所以**不一定成功(**可能让之后又被自己抢到),**不会释放锁**
  11. 1. **join()**:线程插队,一旦插队成功,则**先执行完插入线程的所有任务**
  12. 1. **setDaemon(true)**:将线程设置为**守护线程**(当所有**用户线程结束时**,自动结束,例如垃圾回收机制)
  13. 1. **以下方法必须在同步方法或代码块中由同步监视器(加锁的对象)调用**
  14. 1. **wait()**:使当前线程进入阻塞,并释放锁,等待被唤醒
  15. 1. notify():随机唤醒一个被wait的线程
  16. 1. **notifyall()**:唤醒所有被wait线程
  17. ```java
  18. package review.thread;
  19. /**
  20. * 守护线程
  21. * @Author rainbow
  22. * @Date 2022/5/16 10:40
  23. * @Version 1.0
  24. **/
  25. public class DaemonTest {
  26. public static void main(String[] args) throws InterruptedException {
  27. MyDaemon myDaemon = new MyDaemon();
  28. Thread thread = new Thread(myDaemon);
  29. //将子线程设置为守护进程,当所有的用户线程结束时,会自动结束
  30. thread.setDaemon(true);
  31. thread.start();
  32. Thread1 thread1 = new Thread1();
  33. thread1.start();
  34. /**
  35. * 让子线程执行结束才执行主线程,因为守护线程先start,且join是针对主线程的
  36. *(写在主线程中的),所以守护线程会和子线程一起执行
  37. * 而如果先启动子线程再启动守护线程,则顺序是子线程结束,守护线程和主线程才开始
  38. */
  39. thread1.join();
  40. for (int i = 0; i < 10; i++) {
  41. System.out.println("主线程执行...");
  42. Thread.sleep(500);
  43. }
  44. }
  45. }
  46. class Thread1 extends Thread{
  47. @Override
  48. public void run() {
  49. for (int i = 0; i < 10; i++) {
  50. System.out.println("子线程执行...");
  51. try {
  52. Thread.sleep(500);
  53. } catch (InterruptedException e) {
  54. e.printStackTrace();
  55. }
  56. }
  57. }
  58. }
  59. class MyDaemon implements Runnable{
  60. @Override
  61. public void run() {
  62. while (true){
  63. System.out.println("守护线程执行中...");
  64. try {
  65. Thread.sleep(500);
  66. } catch (InterruptedException e) {
  67. e.printStackTrace();
  68. }
  69. }
  70. }
  71. }

3.线程同步

1.synchronized方式

  1. 保证数据操作的完整性,会导致程序效率降低
  2. 每个对象都对应一个可称为互斥锁的标记,保证在同一时刻只能有一个线程访问对象
  3. 关键字synchronized来与对象的互斥锁联系,当对象被synchronized修饰时,表示任一时刻只能被一个线程访问
  4. synchronized可以修饰方法或代码块,优先使用同步代码块
  5. 非静态同步方法的锁可以是this,也可以是其他对象(并没有其他含义,就是以一个对象作为锁,谁持有这个对象谁就可以操作代码块,类似于钥匙,而谁是钥匙并不重要,只需要始终是同一个把钥匙即可)
  6. 静态同步方法(static修饰的方法)的锁是当前类本身(类名.class)
  7. 静态方法锁默认当前类.class,非静态方法锁默认this
  8. 必须保证锁的对象是多个对象共享的(例如通过继承Thread类实现的线程就不能用this锁,因为他们各自是new的新对象)

    2.Lock锁

  9. jdk5.0新增的接口,能够显式加锁与释放锁,需要使用具体的实现类ReentrantLock

  10. 加锁: lock.lock(),解锁:lock.unlock()
  11. 与synchronized区别是:
    1. synchronized会自动释放锁
    2. lock需要手动的加锁与解锁


3.释放锁

:::danger 以下操作会释放锁

  1. 1. 当前线程的同步方法、同步代码块执行完毕
  2. 1. 在同步方法或代码块中遇到breakreturn
  3. 1. 出现了未处理的异常
  4. 1. 执行了wait()方法

:::

以下操作不会释放锁

  1. 1. 调用sleep()、yield()方法,只会暂停,不会释放锁
  2. 1. 其他线程调用该线程的suspend()方法,将该线程挂起(不推荐使用)