多线程基础

多线程基础


前言

本文主要介绍线程基础问题


提示:以下是本篇文章正文内容,下面案例可供参考

一、进程和线程?

进程: 所有线程的集合
线程: 进程中的一个执行单元

二、同步和异步?

同步: 顺序执行,需要等待结果
异步: 一起执行,不需要等待结果

三、多线程的好处?

一句话:提高程序的执行效率

形象理解:有给一条公路画黄线的任务
单线程==>从起点开始画,路况堵的情况下就画慢一点,等堵车结束,最后画到终点
多线程==>把这条公里分成多段,单核情况下就好比只有一个刷子,哪里堵了,就把刷子拿到不堵的路段先画(cpu调度),最后画完.多核好比多个刷子,多路段一起画,最后完工,提高效率,不用堵那里.

四、多线程创建方法?

1.继承Thread类

class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("用户线程");
    }
}
public class Test01 {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        System.out.println("main线程");
    }
}

2.实现Runable接口

class MyRunable implements Runnable{
    @Override
    public void run() {
        System.out.println("用户线程");
    }
}
public class Test01 {
    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunable());
        thread.start();
        System.out.println("main线程");
    }
}

3.实现Callable接口(有返回值)

class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("用户线程");
        return 1;
    }
}
public class Test01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 根据task,获取结果
        System.out.println("main线程");
        FutureTask<Integer> task = new FutureTask<>(new MyCallable());
        Thread thread = new Thread(task);
        thread.start();
        System.out.println("task结果:"+task.get());
    }
}

五、多线程状态?

在这里插入图片描述

六、守护线程和用户线程?

守护线程: 主线程结束,守护线程直接结束,setDaemon(true)设置成守护线程
用户线程: 主线程结束,用户线程继续执行

七、线程安全?

1. 演示安全问题

public class Test01 implements Runnable {

    private Integer count = 100;

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Test01 test01 = new Test01();
        Thread t1 = new Thread(test01, "窗口1");
        Thread t2 = new Thread(test01, "窗口2");
        t1.start();
        t2.start();
    }
    @Override
    public void run() {
        while (count > 0) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            sale();
        }

    }
    public void sale() {
        System.out.println(Thread.currentThread().getName() + ",在出售第" + (100 - count + 1) + "张票");
        count--;
    }
}

在这里插入图片描述
2. 出现原因

  • JMM内存模型
    JMM定义了线程和主内存的抽象关系: 线程共享变量存在主内存,每个线程有自己的本地内存,本地内存存储了共享变量的副本,如图
    在这里插入图片描述
  • 线程A和线程B通讯过程
    首先线程A把修改过的本地内存副本刷新到主内存,然后,线程B到主内存中读取A改过的结果.
  • 出现两个窗口都卖第一张票的原因
    线程A卖出第一张票,剩余99还没来得及刷新到主内存,线程B开始卖票,B不知道A改了(因为B只能通过主内存知道,而A恰好没来的急刷新到主内存),所有也卖第一张票.

3. 解决安全问题

 // 加 synchronized 关键字,线程同步,一次只能一个线程执行(刷新完毕后其他线程执行)
    public synchronized void sale() {
        // 这个判断是最后一张票,防止出现负数.
        if (count>0){
            System.out.println(Thread.currentThread().getName() + ",在出售第" + (100 - count + 1) + "张票");
            count--;
        }
    }

八、线程死锁?

互相需要对方持有的资源,造成死锁

public class Test02 implements Runnable {
    private Integer count = 100;
    private Boolean flag = true;
    private Object obj = new Object();

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Test02 test02 = new Test02();
        Thread t1 = new Thread(test02, "窗口1");
        Thread t2 = new Thread(test02, "窗口2");
        t1.start();
        Thread.sleep(50);
        test02.flag=false;
        t2.start();

    }

    @Override
    public void run() {
        if (flag) {
            synchronized (obj) {
                while (count > 0) {
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    sale();
                }
            }

        } else {
            sale();
        }


    }

    // 加 synchronized 关键字
    public synchronized void sale() {
        // 这个判断是最后一张票,防止出现负数.
        synchronized (obj) {
            if (count > 0) {
                System.out.println(Thread.currentThread().getName() + ",在出售第" + (100 - count + 1) + "张票");
                count--;
            }
        }
    }
}

在这里插入图片描述
原因
在这里插入图片描述
窗口1持有obj锁,需要this锁
窗口2持有this锁,需要obj锁 造成互相等待,死锁

九、多线程特性?

1. 原子性

一个操作或者多个操作,要么说都只执行,要么说都不执行
例如: A账户向B账户转账100元,A账户减100元,B账户增加100元.原子性就是A减少和B增加必须都成功,不然都失败.

2. 可见性

即内存模型中,某个线程修改了自己本地内存副本必须立刻刷新到主内存,主内存修改对其他线程即为可见性 
Volatile可以实现可见性,禁止指令重排
  • 不可见演示
public class Test04 implements Runnable {
   private Boolean flag = true;

   @Override
   public void run() {
       System.out.println("子线程开始");
       while (flag) {

       };
       System.out.println("子线程结束");
   }
   public void setFlag(){
       this.flag=false;
   }

   public static void main(String[] args) throws InterruptedException {

       Test04 test04 = new Test04();
       Thread thread = new Thread(test04);
       thread.start();
       System.out.println("main线程开始");
       Thread.sleep(50);
       test04.setFlag();
       System.out.println("main线程结束");
   }
}

在这里插入图片描述

  • Volatile解决可见性
// 加上volatile 
 private volatile Boolean flag = true;

在这里插入图片描述
3. 有序性

十、线程通讯?

wait 和notify

public class Test05 {
    public static void main(String[] args) {
        Student student = new Student();
        MyInput myInput = new MyInput(student);
        MyOut myOut = new MyOut(student);
        myInput.start();
        myOut.start();


    }
}

class Student {
    public String name;
    public String sex;
    // false 写 true 读
    public boolean flag = false;
}

class MyInput extends Thread {
    private Student student;

    public MyInput(Student student) {
        this.student = student;
    }

    @Override
    public void run() {
        int count = 0;
        while (true) {
            synchronized (student) {
                if (student.flag) {
                    try {
                        student.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if (count == 0) {
                    student.name = "张三";
                    student.sex = "男";
                } else {
                    student.name = "李四";
                    student.sex = "女";
                }
                count = (count + 1) % 2;
                student.flag = true;
                student.notify();
            }
        }
    }
}

class MyOut extends Thread {
    private Student student;

    public MyOut(Student student) {
        this.student = student;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (student) {
                if (!student.flag) {
                    try {
                        student.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();

                    }
                }
                System.out.println(student.name + "--" + student.sex);
                student.flag = false;
                student.notify();
            }
        }
    }
}

在这里插入图片描述
保证写一个读一个(生产消费模式)

十一、线程池?

1.常见线程池

  • newCachedThreadPool 缓存线程池,有就复用,没有就创建
public class NewCachedThreadPoolTest {

  public static void main(String[] args) {
      // 创建一个可缓存线程池
      ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
      for (int i = 0; i < 10; i++) {
          try {
              // sleep可明显看到使用的是线程池里面以前的线程,没有创建新的线程
              Thread.sleep(1000);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
          cachedThreadPool.execute(new Runnable() {
              @Override
              public void run() {
                  // 打印正在执行的缓存线程信息
                  System.out.println(Thread.currentThread().getName()
                          + "正在被执行");
                  try {
                      // sleep可明显看到使用的是线程池里面以前的线程,没有创建新的线程
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          });
      }
  }
}

在这里插入图片描述

  • newFixedThreadPool 固定数量线程池,线程数量固定
public class Test06 {
   public static void main(String[] args) {
       ExecutorService executorService = Executors.newFixedThreadPool(5);
       for (int i = 0; i < 10; i++) {
           executorService.execute(new Runnable() {
               @Override
               public void run() {
                   System.out.println(Thread.currentThread().getName()+"正在执行");
               }
           });
       }
   }
}

在这里插入图片描述

  • newScheduledThreadPool 定时线程池,可执行一下定时任务
@Slf4j
public class Test06 {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
        log.info("开始");
        scheduledExecutorService.schedule(new Runnable() {
            @Override
            public void run() {
               log.info("延迟1s执行");
            }
        },1, TimeUnit.SECONDS);
    }
}

在这里插入图片描述

 scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
               log.info("每隔1s执行");
            }
        },2,1, TimeUnit.SECONDS);

在这里插入图片描述

  • newSingleThreadExecutor 单线程线程池,只有一个线程
public class Test06 {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"正在执行");
                }
            });
        }
    }
}

在这里插入图片描述

  • 自定义
@Slf4j
public class Test06 {
   public static void main(String[] args) {
       MyTask myTask = new MyTask();
       MyTask myTask1 = new MyTask();
       MyTask myTask2 = new MyTask();
       MyTask myTask3 = new MyTask();
       //初始化队列数量
       ArrayBlockingQueue queue = new ArrayBlockingQueue<Runnable>(10);
       //3 --核心线程数
       // 6 --最大线程数
       // 500s --清除时间
       //queue --阻塞队列
       ThreadPoolExecutor myExcutor = new ThreadPoolExecutor(3, 6, 500, TimeUnit.SECONDS, queue);

       myExcutor.execute(myTask);
       myExcutor.execute(myTask1);
       myExcutor.execute(myTask2);
       myExcutor.execute(myTask3);

   }
}
class MyTask implements Runnable{
   @Override
   public void run() {
       System.out.println(Thread.currentThread().getName()+"正在执行");
       try {
           Thread.sleep(1000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
   }
}

在这里插入图片描述
2. 线程池参数
在这里插入图片描述

3. 线程池流程

1>如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务;
2>如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;
3>如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;
4>如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。
在这里插入图片描述
4. 拒绝策略

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。 ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务
ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务

十二、countDownLatch?

countDownLatch是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就 -1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。

@Slf4j
public class CountDownLatchTest {

  public static void main(String[] args) {
      final CountDownLatch latch = new CountDownLatch(2);
      log.debug("主线程开始执行…… ……");
      //第一个子线程执行
      ExecutorService es1 = Executors.newSingleThreadExecutor();
      es1.execute(new Runnable() {
          @Override
          public void run() {
              try {
                  Thread.sleep(3000);
                  log.debug("子线程:"+Thread.currentThread().getName()+"执行");
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              latch.countDown();
          }
      });
      es1.shutdown();

      //第二个子线程执行
      ExecutorService es2 = Executors.newSingleThreadExecutor();
      es2.execute(new Runnable() {
          @Override
          public void run() {
              try {
                  Thread.sleep(3000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              log.debug("子线程:"+Thread.currentThread().getName()+"执行");
              latch.countDown();
          }
      });
      es2.shutdown();
      log.debug("等待两个线程执行完毕…… ……");
      try {
          latch.await();
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
      log.debug("两个子线程都执行完毕,继续执行主线程");
  }
}

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值