彻底搞懂Java多线程(一)

作者:保护眼睛 时间:2023-08-02 10:42:30 

Java多线程

线程的创建

1.继承Thread

2.实现Runnable

3.实现Callable

使用继承Thread类来开发多线程的应用程序在设计上是有局限性的,因为Java是单继承。

继承Thread类


public class ThreadDemo1 {
   // 继承Thread类 写法1
   static class MyThread extends Thread{
       @Override
       public void run() {
           //要实现的业务代码
       }
   }
   // 写法2
   Thread thread = new Thread(){
       @Override
       public void run() {
           //要实现的业务代码
       }
   };
}

实现Runnable接口


//实现Runnable接口 写法1
class MyRunnable implements Runnable{
   @Override
   public void run() {
       //要实现的业务代码
   }
}
//实现Runnable接口 写法2 匿名内部类
class MyRunnable2 {
   public static void main(String[] args) {
       Thread thread = new Thread(new Runnable() {
           @Override
           public void run() {
               //要实现的业务代码
           }
       });
   }
}

实现Callable接口(Callable + FutureTask 创建带有返回值的线程)


package ThreadDeom;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* user:ypc;
* date:2021-06-11;
* time: 17:34;
*/
//创建有返回值的线程 Callable + Future
public class ThreadDemo2 {
   static class MyCallable implements Callable<Integer>{
       @Override
       public Integer call() throws Exception {
           return 0;
       }
   }
   public static void main(String[] args) throws ExecutionException, InterruptedException {
       //创建Callable子对象
       MyCallable myCallable = new MyCallable();
       //使用FutureTask 接受 Callable
       FutureTask<Integer> futureTask = new FutureTask<>(myCallable);
       //创建线程并设置任务
       Thread thread = new Thread(futureTask);
       //启动线程
       thread.start();
       //得到线程的执行结果
       int num = futureTask.get();
   }
}

也可以使用lambda表达式


class ThreadDemo21{
   //lambda表达式
   Thread thread = new Thread(()-> {
       //要实现的业务代码
   });
}

Thread的构造方法

彻底搞懂Java多线程(一)

线程常用方法

获取当前线程的引用、线程的休眠


class Main{
   public static void main(String[] args) throws InterruptedException {
       Thread.sleep(1000);
       //休眠1000毫秒之后打印
       System.out.println(Thread.currentThread());
       System.out.println(Thread.currentThread().getName());
   }
}

彻底搞懂Java多线程(一)


package ThreadDeom;
/**
* user:ypc;
* date:2021-06-11;
* time: 18:38;
*/
public class ThreadDemo6 {
   public static void main(String[] args) throws InterruptedException {
       Thread thread = new Thread(new Runnable() {
           @Override
           public void run() {
               System.out.println("线程的ID:" + Thread.currentThread().getId());
               System.out.println("线程的名称:" + Thread.currentThread().getName());
               System.out.println("线程的状态:" + Thread.currentThread().getState());
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       },"线程一");
       thread.start();
       Thread.sleep(100);
       //打印线程的状态
       System.out.println("线程的状态:"+thread.getState());
       System.out.println("线程的优先级:"+thread.getPriority());
       System.out.println("线程是否存活:"+thread.isAlive());
       System.out.println("线程是否是守护线程:"+thread.isDaemon());
       System.out.println("线程是否被打断:"+thread.isInterrupted());
   }
}

彻底搞懂Java多线程(一)

线程的等待

假设有一个坑位,thread1 和 thread2 都要上厕所。一次只能一个人上,thread2只能等待thread1使用完才能使用厕所。就可以使用join()方法,等待线程1执行完,thread2在去执行。👇


package ThreadDeom;
/**
* user:ypc;
* date:2021-06-12;
* time: 10:48;
*/
public class ThreadDemo13 {
   public static void main(String[] args) throws InterruptedException {
       Runnable runnable = new Runnable() {
           @Override
           public void run() {
               System.out.println(Thread.currentThread().getName()+"🚾");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               System.out.println(Thread.currentThread().getName()+"出来了");
           }
       };
       Thread t1 = new Thread(runnable,"thread1");
       t1.start();
       //t1.join();
       Thread t2 = new Thread(runnable,"thread2");
       t2.start();
   }
}

彻底搞懂Java多线程(一)

没有join()显然是不行的。加上join()之后:

彻底搞懂Java多线程(一)

线程的终止

1.自定义实现线程的终止


package ThreadDeom;
/**
* user:ypc;
* date:2021-06-12;
* time: 9:59;
*/
public class ThreadDemo11 {
   private static boolean flag = false;
   public static void main(String[] args) throws InterruptedException {
       Thread thread = new Thread(new Runnable() {
           @Override
           public void run() {
               while (!flag){
                   System.out.println("我是 : " + Thread.currentThread().getName() + ",我还没有被interrupted呢");
                   try {
                       Thread.sleep(100);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
               System.out.println("我是 "+Thread.currentThread().getName()+",我被interrupted了");
           }
       },"thread");
       thread.start();
       Thread.sleep(300);
       flag = true;
   }
}

彻底搞懂Java多线程(一)

2.使用Thread的interrupted来中断


package ThreadDeom;
/**
* user:ypc;
* date:2021-06-12;
* time: 9:59;
*/
public class ThreadDemo11 {
//    private static boolean flag = false;
   public static void main(String[] args) throws InterruptedException {
       Thread thread = new Thread(new Runnable() {
           @Override
           public void run() {
               while (!Thread.interrupted()){
                   System.out.println("我是 : " + Thread.currentThread().getName() + ",我还没有被interrupted呢");
                   try {
                       Thread.sleep(100);
                   } catch (InterruptedException e) {
//                        e.printStackTrace();
                       break;
                   }
               }
               System.out.println("我是 "+Thread.currentThread().getName()+",我被interrupted了");
           }
       },"thread");
       thread.start();
       Thread.sleep(300);
       thread.interrupt();
//        flag = true;
   }
}

彻底搞懂Java多线程(一)

3.Thraed.interrupted()方法和Threaed.currentThread().interrupt()的区别

Thread.interrupted()方法第一次接收到终止的状态后,之后会将状态复位,Thread.interrupted()是静态的,是全局的。

Threaed.currentThread().interrupt()只是普通的方法。

Thraed.interrupted()方法


package ThreadDeom;
/**
* user:ypc;
* date:2021-06-12;
* time: 10:32;
*/
public class ThreadDemo12 {
   public static void main(String[] args) throws InterruptedException {
       Thread thread = new Thread(() ->{
           for (int i = 0; i < 10; i++) {
               System.out.println(Thread.interrupted());
           }
       });
       thread.start();
       thread.interrupt();
   }
}

彻底搞懂Java多线程(一)

Threaed.currentThread().interrupt()


package ThreadDeom;
/**
* user:ypc;
* date:2021-06-12;
* time: 10:32;
*/
public class ThreadDemo12 {
   public static void main(String[] args) throws InterruptedException {
       Thread thread = new Thread(() ->{
           for (int i = 0; i < 10; i++) {
//                System.out.println(Thread.interrupted());
               System.out.println(Thread.currentThread().isInterrupted());
           }
       });
       thread.start();
       thread.interrupt();
   }
}

彻底搞懂Java多线程(一)

yield()方法

让出CPU的执行权


package ThreadDeom;
/**
* user:ypc;
* date:2021-06-12;
* time: 11:47;
*/
public class ThreadDemo15 {
   public static void main(String[] args) {
       Thread thread1 = new Thread(() -> {
           for (int i = 0; i < 100; i++) {
               Thread.yield();
               System.out.println("thread1");
           }
       });
       thread1.start();
       Thread thread2 = new Thread(() -> {
           for (int i = 0; i < 100; i++) {
               System.out.println("thread2");
           }
       });
       thread2.start();
   }
}

彻底搞懂Java多线程(一)

线程的状态

彻底搞懂Java多线程(一)

打印出线程的所有的状态,所有的线程的状态都在枚举中。👇


package ThreadDeom;
/**
* user:ypc;
* date:2021-06-12;
* time: 11:06;
*/
public class ThreadDemo14 {
   public static void main(String[] args) {
       for (Thread.State state: Thread.State.values()) {
           System.out.println(state);
       }
   }
}

彻底搞懂Java多线程(一)

  • NEW 创建了线程但是还没有开始工作

  • RUNNABLE 正在Java虚拟机中执行的线程

  • BLOCKED 受到阻塞并且正在等待某个监视器的锁的时候所处的状态

  • WAITTING 无限期的等待另一个线程执行某个特定操作的线程处于这个状态

  • TIME_WAITTING 有具体等待时间的等待

  • TERMINATED 已经退出的线程处于这种状态


package ThreadDeom;
/**
* user:ypc;
* date:2021-06-12;
* time: 11:06;
*/
class TestThreadDemo{
   public static void main(String[] args) throws InterruptedException {
       Thread thread = new Thread(new Runnable() {
           @Override
           public void run() {
               try {
                   Thread.sleep(2000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       });
       System.out.println(thread.getState());
       thread.start();
       System.out.println(thread.getState());
       Thread.sleep(100);
       System.out.println(thread.getState());
       thread.join();
       System.out.println(thread.getState());
   }
}

彻底搞懂Java多线程(一)

线程的优先级

在Java中线程 的优先级分为1 ~ 10 一共十个等级


package ThreadDeom;
/**
* user:ypc;
* date:2021-06-11;
* time: 21:22;
*/
public class ThreadDemo9 {
   public static void main(String[] args) {
       for (int i = 0; i < 5; i++) {
           Thread t1 = new Thread(new Runnable() {
               @Override
               public void run() {
                   System.out.println("t1");
               }
           });
           //最大优先级
           t1.setPriority(10);
           t1.start();
           Thread t2 = new Thread(new Runnable() {
               @Override
               public void run() {
                   System.out.println("t2");
               }
           });
           //最小优先级
           t2.setPriority(1);
           t2.start();
           Thread t3 = new Thread(new Runnable() {
               @Override
               public void run() {
                   System.out.println("t3");
               }
           });
           t3.setPriority(1);
           t3.start();
       }
   }
}

彻底搞懂Java多线程(一)

线程的优先级不是绝对的,只是给程序的建议。

线程之间的优先级具有继承的特性,如果A线程启动了B线程,那么B的线程的优先级与A是一样的。👇


package ThreadDeom;
/**
* user:ypc;
* date:2021-06-11;
* time: 20:46;
*/
class ThreadA extends Thread{
   @Override
   public void run() {
       System.out.println("ThreadA优先级是:" + this.getPriority());
       ThreadB threadB = new ThreadB();
       threadB.start();
   }
}
class ThreadB extends ThreadA{
   @Override
   public void run() {
       System.out.println("ThreadB的优先级是:" + this.getPriority());
   }
}
public class ThreadDemo7 {
   public static void main(String[] args) {
       System.out.println("main线程开始的优先级是:" + Thread.currentThread().getPriority());  
       System.out.println("main线程结束的优先级是:" + Thread.currentThread().getPriority());
       ThreadA threadA = new ThreadA();
       threadA.start();
   }
}

彻底搞懂Java多线程(一)

再看👇


package ThreadDeom;
/**
* user:ypc;
* date:2021-06-11;
* time: 20:46;
*/
class ThreadA extends Thread{
   @Override
   public void run() {
       System.out.println("ThreadA优先级是:" + this.getPriority());
       ThreadB threadB = new ThreadB();
       threadB.start();
   }
}
class ThreadB extends ThreadA{
   @Override
   public void run() {
       System.out.println("ThreadB的优先级是:" + this.getPriority());
   }
}
public class ThreadDemo7 {
   public static void main(String[] args) {
       System.out.println("main线程开始的优先级是:" + Thread.currentThread().getPriority());
       Thread.currentThread().setPriority(9);
       System.out.println("main线程结束的优先级是:" + Thread.currentThread().getPriority());
       ThreadA threadA = new ThreadA();
       threadA.start();
   }
}

结果为👇

彻底搞懂Java多线程(一)

守护线程

Java中有两种线程:一种是用户线程,一种就是守护线程。

什么是守护线程?守护线程是一种特殊的线程,当进程中不存在用户线程的时候,守护线程就会自动销毁。典型的守护线程就是垃圾回收线程,当进程中没有了非守护线程,则垃圾回收线程也就没有存在的必要了。

Daemon线程的作用就是为其他线程的运行提供便利的。👇


package ThreadDeom;
/**
* user:ypc;
* date:2021-06-11;
* time: 21:06;
*/
public class ThreadDemo8 {
   static private int i = 0;
   public static void main(String[] args) throws InterruptedException {
       Thread thread = new Thread(new Runnable() {
           @Override
           public void run() {
               while (true){
                   i++;
                   System.out.println(i);
                   try {
                       Thread.sleep(1000);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
           }
       });
       //设置守护线程
       thread.setDaemon(true);
       thread.start();
       Thread.sleep(5000);
       System.out.println("我是守护线程thread 当用户线程执行完成后 我也就销毁了😭哭了");
   }
}

彻底搞懂Java多线程(一)

注意:守护线程的设置必须放在start()之前,否则就会报错。

彻底搞懂Java多线程(一)

在守护线程中创建的线程默认也是守护线程。


package ThreadDeom;
/**
* user:ypc;
* date:2021-06-12;
* time: 9:35;
*/
public class ThreadDemo10 {
   public static void main(String[] args) {
       Thread thread1 = new Thread(()->{
           Thread thread2 = new Thread(() -> {
           },"thread2");
           System.out.println("thread2是守护线程吗?:" + thread2.isDaemon());
       },"thread1");
       System.out.println("thread1是守护线程吗?:" + thread1.isDaemon());
       //thread1.setDaemon(true);
       thread1.start();
      // System.out.println("thread1是守护线程吗?:" + thread1.isDaemon());
   }
}

彻底搞懂Java多线程(一)

再看👇


package ThreadDeom;
/**
* user:ypc;
* date:2021-06-12;
* time: 9:35;
*/
public class ThreadDemo10 {
   public static void main(String[] args) {
       Thread thread1 = new Thread(()->{
           Thread thread2 = new Thread(() -> {
           },"thread2");
           System.out.println("thread2是守护线程吗?:" + thread2.isDaemon());
       },"thread1");
       System.out.println("thread1是守护线程吗?:" + thread1.isDaemon());
       thread1.setDaemon(true);
       thread1.start();
       System.out.println("thread1是守护线程吗?:" + thread1.isDaemon());
   }
}

彻底搞懂Java多线程(一)

线程组

为了便于对某些具有相同功能的线程进行管理,可以把这些线程归属到同一个线程组中,线程组中既可以有线程对象,也可以有线程组,组中也可以有线程。使用线程模拟赛跑


public class ThreadDemo5 {
   //线程模拟赛跑(未使用线程分组)
   public static void main(String[] args) {
       Thread t1 = new Thread(new Runnable() {
           @Override
           public void run() {
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               System.out.println(Thread.currentThread().getName() + "到达了终点");
           }
       }, "选手一");
       Thread t2 = new Thread(new Runnable() {
           @Override
           public void run() {
               try {
                   Thread.sleep(1200);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               System.out.println(Thread.currentThread().getName() + "到达了终点");
           }
       }, "选手二");
       t1.start();
       t2.start();
       System.out.println("所有选手到达了终点");
   }
}

运行结果:

彻底搞懂Java多线程(一)

不符合预期效果,就可以使用线程组来实现


package ThreadDeom;
/**
* user:ypc;
* date:2021-06-11;
* time: 18:24;
*/
class ThreadGroup1 {
   //线程分组模拟赛跑
   public static void main(String[] args) {
       ThreadGroup threadGroup = new ThreadGroup("Group");
       Thread t1 = new Thread(threadGroup, new Runnable() {
           @Override
           public void run() {
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               System.out.println("选手一到达了终点");
           }
       });
       Thread t2 = new Thread(threadGroup, new Runnable() {
           @Override
           public void run() {
               try {
                   Thread.sleep(1200);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               System.out.println("选手二到达了终点");
           }
       });
       t2.start();
       t1.start();
       while (threadGroup.activeCount() != 0) {
       }
       System.out.println("所有选手到达了终点");
   }
}

彻底搞懂Java多线程(一)

线程组常用的方法

彻底搞懂Java多线程(一)

线程安全问题

来看单线程情况下让count分别自增和自减10000次


package ThreadDeom;
/**
* user:ypc;
* date:2021-06-12;
* time: 12:03;
*/
class Counter {
   private static int count = 0;
   public void increase(){
       for (int i = 0; i < 10000; i++) {
           count++;
       }
   }
   public void decrease(){
       for (int i = 0; i < 10000; i++) {
           count--;
       }
   }
   public int getCount(){
       return count;
   }
}
public class ThreadDemo16 {
   public static void main(String[] args) {
       //单线程
       Counter counter = new Counter();
       counter.increase();
       counter.decrease();
       System.out.println(counter.getCount());
   }
}

结果符合预期

彻底搞懂Java多线程(一)

如果想使程序的执行速度快,就可以使用多线程的方式来执行。在来看多线程情况下的问题


public class ThreadDemo16 {
   public static void main(String[] args) throws InterruptedException {
       //多线程情况下
       Counter counter = new Counter();
       Thread thread1 = new Thread(()->{
           counter.decrease();
       });
       Thread thread2 = new Thread(()->{
           counter.increase();
       });
       thread1.start();
       thread2.start();
       thread1.join();
       thread2.join();
       System.out.println(counter.getCount());
               /*
       //单线程
       Counter counter = new Counter();
       counter.increase();
       counter.decrease();
       System.out.println(counter.getCount());
        */
   }
}

执行结果:

彻底搞懂Java多线程(一)

彻底搞懂Java多线程(一)

彻底搞懂Java多线程(一)

每次的执行结果是不一样的。这就是多线程的不安全问题

彻底搞懂Java多线程(一)

预期的结果是0,但结果却不是。线程不安全问题的原因:

  • 1.CPU的抢占式执行

  • 2.多个线程共同操作一个变量

  • 3.内存可见性

  • 4.原子性问题

  • 5.编译器优化(指令重排)

多个线程操作同一个变量

如果多个线程操作的不是一个变量,就不会发生线程的不安全问题,可以将上面的代码修改如下:👇


public class ThreadDemo16 {
   static int res1 = 0;
   static int res2 = 0;
   public static void main(String[] args) throws InterruptedException {
       Counter counter = new Counter();
       Thread thread1 = new Thread(new Runnable() {
           @Override
           public void run() {
               res1 = counter.getCount();
           }
       });
       Thread thread2 = new Thread(new Runnable() {
           @Override
           public void run() {
               res2 = counter.getCount();
           }
       });
       System.out.println(res1 + res2);
/*
       //多线程情况下
       Counter counter = new Counter();
       Thread thread1 = new Thread(()->{
           counter.decrease();
       });
       Thread thread2 = new Thread(()->{
           counter.increase();
       });
       thread1.start();
       thread2.start();
       thread1.join();
       thread2.join();
       System.out.println(counter.getCount());
       */
               /*
       //单线程
       Counter counter = new Counter();
       counter.increase();
       counter.decrease();
       System.out.println(counter.getCount());
        */
   }
}

这样就可以了:

彻底搞懂Java多线程(一)

内存不可见问题:看下面的代码,是不是到thread2执行的时候,就会改变num的值,从而终止了thread1呢?


package ThreadDeom;
import java.util.Scanner;
/**
* user:ypc;
* date:2021-06-12;
* time: 13:03;
*/
public class ThreadDemo17 {
   private static int num = 0;
   public static void main(String[] args) {
       Thread thread1 = new Thread(new Runnable() {
           @Override
           public void run() {
               while (num == 0){}
           }
       });
       thread1.start();
       Thread thread2 = new Thread(new Runnable() {
           @Override
           public void run() {
               Scanner scanner = new Scanner(System.in);
               System.out.println("输入一个数字来终止线程thread1");
               num = scanner.nextInt();
           }
       });
       thread2.start();
   }
}

结果是不能的:

彻底搞懂Java多线程(一)

输入一个数字后回车,并没有让thread1的循环结束。这就是内存不可见的问题。

原子性的问题

上面的++和–操作其实是分三步来执行的

彻底搞懂Java多线程(一)

假设在第二部的时候,有另外一个线程也来修改值,那么就会出现脏数据的问题了。

所以就会发生线程的不安全问题

编译器优化编译器的优化会打乱原本程序的执行顺序,就有可能导致线程的不安全问题发生。在单线程不会发生线程的不安全问题,在多线程就可能会不安全。

volatile关键字

可以使用volatile关键字,这个关键字可以解决指令重排和内存不可见的问题。

彻底搞懂Java多线程(一)

加上volatile关键字之后的运行结果

彻底搞懂Java多线程(一)

但是volatile关键字不能解决原子性的问题👇:


package ThreadDeom;
/**
* user:ypc;
* date:2021-06-12;
* time: 14:02;
*/
class Counter1 {
   private static volatile int count = 0;
   public void increase() {
       for (int i = 0; i < 10000; i++) {
           count++;
       }
   }
   public void decrease() {
       for (int i = 0; i < 10000; i++) {
           count--;
       }
   }
   public int getCount() {
       return count;
   }
}
public class ThreadDemo18 {
   public static void main(String[] args) throws InterruptedException {
       Counter1 counter1 = new Counter1();
       Thread thread1 = new Thread(new Runnable() {
           @Override
           public void run() {
               counter1.decrease();
           }
       });
       Thread thread2 = new Thread(() -> {
           counter1.increase();
       });
       thread1.start();
       thread2.start();
       thread1.join();
       thread2.join();
       System.out.println(counter1.getCount());
   }
}

彻底搞懂Java多线程(一)

彻底搞懂Java多线程(一)

来源:https://blog.csdn.net/qq_45859087/article/details/117821279

标签:Java,多线程
0
投稿

猜你喜欢

  • Java 获取网络302重定向URL的方法

    2022-03-25 15:19:46
  • Spring依赖注入的三种方式小结

    2022-08-09 15:56:41
  • Java中Lombok常用注解分享

    2023-06-13 01:32:48
  • Win10下C# DateTime出现星期几问题的解决方法

    2023-01-12 16:42:04
  • C#移除字符串中的不可见Unicode字符 案例代码

    2023-04-28 19:06:06
  • SpringBoot之@Value获取application.properties配置无效的解决

    2023-09-15 22:37:55
  • 详解JAVA高质量代码之数组与集合

    2022-03-31 16:42:07
  • java中sleep方法和wait方法的五个区别

    2023-08-27 18:37:23
  • C#使用BackgroundWorker控件

    2022-10-25 05:06:35
  • android学习笔记之View的滑动

    2022-12-30 07:51:36
  • Java三大特性之多态详解

    2022-11-07 14:24:40
  • MyBatis动态SQL表达式详解

    2023-11-29 00:42:36
  • Java内存模型final的内存语义

    2023-06-05 08:02:25
  • 一篇文章弄懂kotlin的扩展方法

    2022-06-05 08:06:20
  • JAVA中实现原生的 socket 通信机制原理

    2021-08-08 03:06:01
  • SpringCloud HystrixDashboard服务监控详解

    2021-12-16 03:35:47
  • Android实现自由拖动并显示文字的悬浮框

    2023-06-08 06:30:39
  • springmvc 中dao层和service层的区别说明

    2022-07-26 00:55:41
  • C#中out保留字用法实例分析

    2021-07-28 18:44:54
  • Java 六类运算符详解

    2023-08-27 20:37:37
  • asp之家 软件编程 m.aspxhome.com