多线程及线程池

多线程基本概念

  1. 进程:类似一个软件
  2. 线程:软件中的执行路径

线程调度

我们说4核也好,8核也好,这是对计算机更合理的分配,比如一个🧠(cpu),它运行的很快,同一段时间做很多事情,但是他仍旧是每次只做一件事。

打个比方:一人一年,写书,创业,旅游,恋爱。我们需要了解的是计算机的处理速度是远快于人的。

所以
人需要学会灵活运用计算机的算力,锻炼自己如何学习架构一个完备的软件能力,而不是和计算机拼手速。

再举一个栗子:
1000人 8个岗位
1.不排队,就是切换
2.排队,轮流做
哪个更加有效率?
试想一下不排队,切换时候用的时间。接下来说说下面的同步异步。

同步与异步
同步:排队去做某件事,线程安全
异步:同时执行,线程不安全
想象一下:夏天排队买西瓜 (同步)
西瓜车倒了,去抢西瓜。(异步)

并发&并行
并发:多个事件在同一个时间段内发生
并行:多个事件在同一个时刻发生(同时)
再举栗子 写书,创业,旅游,恋爱 今年在Allen发生(并发)
A,B在2021.6.31 在云南大理相遇(并行)

常用方法

Thread线程里的run方法

MyThread 去重写Thread中的run方法
我们想用该方法,就要调用start()
注意:如果我们采用的是run(),那就是先
运行MyThread线程后,再去运行Main对象的方法

实现Runnable方法,重写里面的run方法
创建任务对象 MyRunnable
创建线程,分配任务
执行线程
1.创建任务,给线程分配的方式实现多线程
2.避免单继承所带来的局限
3.任务与线程分离,提高程序的健壮性
4.后续学习的线程池技术,接受Runnable类型的任务,不接受Thread类型的线程

匿名内部类继承Thread的方式

new  Thread(new Runnable(){
     public void run(){
      System.out.print("哥斯拉大战金刚");
     }
}).start();

注意:
线程检测-1,从而return 释放资源
sleep() 休眠操作
Deamon线程(守护线程),用户线程

线程休眠sleep:注意此处的millis有1000ms为1s

 	 public class Demo4 {
public static void main(String[] args) throws InterruptedException {
   for (int i = 0 ; i < 10; i++ ){
       System.out.println("Build your dream.");
       Thread.sleep(1);
   }
}

}

线程阻塞
缺点:耗时
发生在:文件读取

线程中断:是否应该结束,由其自身决定

public class Demo5 {
    public static void main(String[] args) {
         Thread ss = new Thread(new MyRunnable1());
         ss.start();
         //如何获取当前的线程名称?
     //  Thread.currentThread().getname()
         for (int j = 0; j < 5; j++) {
            System.out.println(Thread.currentThread().getName()+j);
         try {
              Thread.sleep(1000);
             } catch (InterruptedException e) {
             e.printStackTrace();
         }

           }
        ss.interrupt();    //main进程向子线程发出提示命令,此处如果
                              
}
static class MyRunnable1 implements  Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println("To be" + i);

            try {
                Thread.sleep(1000);
            } catch (Exception e) {
//                e.getStackTrace();
                System.out.println("收到命令,准备赴死");
                return;    //返回,程序终止       没有return,多加载一个
                }
           }
        }
    }
}

在这里插入图片描述

为什么我们此处用try-catch命令?
Runnnable接口没有声明异常,那么子线程就不抛出比父线程更大的异常?

使用t1.interrupt 标识块目标是?
提醒子线程死亡,此时我们虽然跳转到catch块,但就是不报错
return才会把资源释放了

注意:父线程需要在子线程之前结束,才能触发子线程的结束命令。具体可参见上述代码。

守护线程:守护用户线程,最后一个用户线程结束时,所有守护线程自动死亡。
骑士 在前 ss.setDaemon(true);

用户线程:当一个进程不包含任何的存活的用户线程时,进程结束
公主 在后 ss.start();
再次强调:守护线程在用户线程之前

线程安全问题
售票系统
卖票,休眠,减去票数,出票
我们会在这两个过程里面发现BC线程在A线程休眠的时候抢到了票(因为票数Ticket还没完成,自减)

package ThreadsDemo;

/**
 * @Author:Allen
 * @Date:3/23/2021 3:36 PM
 */
 
public class Demo6 {
    public static void main(String[] args) {
        Thread s1 = new Thread(new MyrRunnable());
        s1.start();
        new Thread(s1).start();
        new Thread(s1).start();  //两个线程同时去争夺资源,想象两头狮子强地盘,如果来第三头呢?
    }

   static  class MyrRunnable implements Runnable{
       private int ticket =10;
       @Override
       public void run() {

           for (int i =0;i < 10;i++){
               System.out.println("准备取票请稍后"+Thread.currentThread().getName());
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               ticket--;
               System.out.println("取票成功,还剩"+ticket);
           }
       }
   }
}

Tips:
1.ticket不放在 @Override后面
2. Runnable s1 = new MyrRunnable();
new Thread(s1).start();
new Thread(s1).start();
new Thread(s1).start();

总结:线程不安全更多出现在资源少,大家争夺比较密集的情况。

隐式锁

1.同步代码块
synchronized(锁对象)
while(true)

方案一,用锁对象去锁 if判断票的量
方案二,不合理的解决是每个进程都有自己的一把锁 

采用方案二,如下图重写的run方法,很明显就是每个创建的对象都可以拥有一把锁,那么就是不安全的。

试想一下: 小伙子们不用手抓饭,而是一伙人用碗一起乘饭。

public void run() {
       Object o =new Object();
       while (true) {
           synchronized (o) {   //自带“饭碗”,但仍旧不安全
               if (ticket > 0) {
                   System.out.println("准备取票请稍后" + Thread.currentThread().getName());
                   try {
                       Thread.sleep(100);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   ticket--;
                   System.out.println("取票成功,还剩" + ticket);
               } else {
                   break;
               }
           }
       }
      }

可以看见出现-1情况在这里插入图片描述
lock锁放在if判断语句之外

   package ThreadsDemo;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/** 显示锁  思考lock锁应该存放的位置  如果lock锁放在if判断语句之后
 *  1.意味着每个人去抢饭,然后抢到后用碗乘自己在那吃饭,吃完后把碗扔了
 *  2. 每个人抢饭,抢到饭的把锅扣着,别人吃不了
 * @Author:Allen
 * @Date:3/23/2021 3:36 PM
 */
public class Demo7 {
    public static void main(String[] args) {
        Runnable s1= new MyrRunnable();
        Thread s2 = new Thread(new MyrRunnable());
        new Thread(s1).start();
        new Thread(s1).start();
        new Thread(s1).start();
    }

   static  class MyrRunnable implements Runnable{
       private int ticket =10;
       private Lock l = new ReentrantLock(true);
       //private
       @Override
       public void run() {

         while(true){
             l.lock();
            if (ticket > 0) {

                System.out.println("准备取票请稍后" + Thread.currentThread().getName());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                ticket--;
                System.out.println("取票成功,还剩" + ticket);

                } else {
                break;
              }
             l.unlock();
           }
       }
   }
}

在这里插入图片描述

 情况一: 如果我们创建三个对象,他们是不安全的,会同时去抢进程	  
 情况二:  synchronized(this){}这个出现,我们可以想象一个挡了两条路,
         其他的都走不了

显示锁 Lock
给任务run一把锁

注意:  Lock不会自动解除,需要调用lock方法,之后解锁lock方法 
           Synchronize自动解除

显示锁和隐式锁之间的区别?

     Synchronize        vs               Lock
     悲观锁                              乐观锁

公平锁,非公平锁?

先来先得,公平锁

死锁 线程是否冲突?

是否产生  A等B进程执行,B等A进程执行的情况 

第三种线程实现的方式,Callable

package ThreadsDemo;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/** 
 * @Author:Allen
 * @Date:3/25/2021 7:28 PM
 */
public class Demo10 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable myCallable = new MyCallable();
        FutureTask futureTask = new FutureTask(myCallable);
        System.out.println(futureTask.isDone());
        new Thread(futureTask).start();
        Integer i = (Integer) futureTask.get();
        System.out.println("--------------------"+i);
        System.out.println(futureTask.cancel(true));
       for (int j = 0 ; j < 10 ;j++){
           System.out.println(j);
       }

    }
    static class MyCallable implements Callable{
        @Override
        public Integer call() throws Exception {
            for (int i = 0 ;  i < 10 ; i++){
                System.out.println(i);
            }
          return 1;
        }
    }
}

wait notify
多线程通信问题,生产者,消费者

package ThreadsDemo;

/**
 * @Author:Allen
 * @Date:3/25/2021 4:12 PM
 */
public class Demo9 {
    public static void main(String[] args) {
      Food food = new Food();
      new Cook(food).start();
      new Waiter(food).start();

    }
    static class Cook extends Thread{
        private  Food food;
        public Cook(Food food) {
            this.food = food;
        }
        @Override
        public void run() {
          for (int i = 0;i<50; i++){
             if (i%2==0){
                food.setNameAndTaste("太湖白鱼","鲜");
             }else {
                food.setNameAndTaste("川系鲈鱼","辣");
             }
          }

        }
    }
    static class Waiter extends Thread{
         private  Food food;
        public Waiter(Food food) {
            this.food = food;
        }

        @Override
        public void run() {
            for (int j = 0; j < 50; j++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                food.get();
            }
        }
    }
    static  class Food{
        private String name;
        private String taste;
        private Boolean flag =true;
        public synchronized void setNameAndTaste(String name,String taste) {
             if(flag) {
                this.name = name;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.taste = taste;    //此时厨师做饭结束后,使flag为false,唤起waiter进程,                   //自己准备“睡觉”
                
                flag = false;   
                this.notify();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        public synchronized void get() {
           // if(!flag) {
                System.out.println("Waiter takes:" + name + ",it tastes:" + taste);
                flag=true;
                this.notify();  //waiter调用的get方法,令flag为true,唤醒厨师做饭,自己准备去休眠
               try {
                   this.wait();
               } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
线程的六个状态,手绘版本

、

创建线程,
创建任务,
执行任务,
关闭线程

线程池

缓存线程池

package ThreadsDemo;

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

/**  缓存线程池    这里我们有一个 排队的概念:
 *  澡堂AB位置,AB同学先到,在澡堂淋浴。随后C同学也到,排队等A同学洗好澡后,在A位置淋浴
 * @Author:Allen
 * @Date:3/25/2021 7:51 PM
 */
public class Demo11 {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(new Runnable() {
            @Override
              public void run() {
                System.out.println(Thread.currentThread().getName()+":The Shawshank Redemption");
            }
                });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+":Forrest Gump");
            }
        });
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+":Summer!");
            }
        });

    }
}

定长线程池
try catch的原因是什么?
打印后再去执行休眠操作
package ThreadsDemo;

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

/** 定长线程池
 * @Author:Allen
 * @Date:3/26/2021 11:12 AM
 */
public class Demo12 {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.execute(new Runnable() {

            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Love you three Thousand and one times");
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Love you three Thousand and two times");
            }
        });
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Love you three Thousand and three times ");
            }
        });


    }
}

在这里插入图片描述

单线程池

package ThreadsDemo;

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

/**
 * @Author:Allen
 * @Date:3/26/2021 2:09 PM
 */
public class Demo13 {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"付出不亚于任何人的努力,热爱工作!");
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"付出不亚于任何人的努力,热爱工作把!");
            }
        });

    }
}

周期定长线程池
1.参数一,任务
2.参数二,延迟时长数字(第一次的执行时间)
3.参数三,周期时长数字(每间隔多久执行一次)
4.参数四,时长数字的单位

package ThreadsDemo;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @Author:Allen
 * @Date:3/26/2021 2:14 PM
 */
public class Demo14 {
    public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
        service.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("动机至善,私信了无!");
            }
        },2,TimeUnit.SECONDS);
    }
}

Lambda表达式
函数式编程思想
面对对象,创建对象调用方法
方法一 匿名内部类,简化书写的过程
方法二 Lambda表达式 ()-> sout
函数式编程

	在主函数中调用一个	Lambda表达式
	定义一个抽象方法,定义一个接口(一个接口采用一个函数)

package ThreadsDemo;

/**

  • @Author:Allen
  • @Date:3/26/2021 2:32 PM
    */
    public class Demo16 {
    public static void main(String[] args) {
/*  法一  匿名对象
 Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Thunder!");
            }
        });
          thread.start();
*/
        /* 法二  函数式编程
 Thread thread = new Thread(()-> System.out.println("Love!"));
 thread.start();
 */
       print((int x, int y)-> {
               return x+y;
           },100,200);
    }
    public static void print(MyMath m,int x,int y){

       int num  =  m.sum(x,y);
        System.out.println(num);
    }

    static  interface   MyMath{
         int sum(int x,int y);
    }
}

总结:多线程需掌握三种实现方式,Thread,Runnable(与线程池技术紧密联系),Callable
Synchronize和Lock的区别,前者自动释放,后者手动
线程池需要把握,线程与任务的关系

感谢你耐心的阅读,下期见

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值