JavaEE_day_23_死锁、线程通信、线程池、懒汉多线程优化

目录

  1. 守护线程
  2. 定时器 Timer
  3. 锁对象
  4. 死锁
  5. 挂起/唤醒
  6. 线程池
  7. 懒汉优化(多线程)

1.守护线程

简单来说,就是没有任何一个线程的时候,JVM就需要退出了,这个时候守护线程也会退出,主要完成垃圾回收等功能>
在没有其他线程存在时,守护线程自动退出,坚持到最后一个结束

  • 设置守护线程
//Thread.setDameon() 
//true启动守护线程,false关闭守护线程
t1.setDaemon(true);

2.定时器 Timer

计划任务,只要有一个任务监听
就会是一个线程
1 执行任务的类 , 2 执行任务起始时间 3 执行任务间隔时间

public static void main(String[] args) throws ParseException {
  //创建计时器对象 
  Timer t= new Timer();
  //创建线程实现类
  //一个实现类对象就是一个线程
  processor_02 p1 = new processor_02();
  processor_02 p2 = new processor_02();
  processor_02 p3 = new processor_02();
  //设置时间格式
  SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM HH:mm:ss SSS");
  //将实现类当做一个线程每隔一段时间进行输出
  t.schedule(p1, sdf.parse("2020-3 00:00:00 000"), 1000);
  t.schedule(p2, sdf.parse("2020-3 00:00:00 000"), 1000);
  t.schedule(p3, sdf.parse("2020-3 00:00:00 000"), 1000);
 }
}
class processor_02 extends TimerTask{
 @Override
 public void run() {
  //创建时间对象
  Date d = new Date();
  //时间格式
  SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM HH:mm:ss SSS");
  //打印时间格式
  System.out.println(sdf.format(d));
 }

3.锁对象

由于对象中有一个方法加锁,那么,在这个线程访问还没结束时,其他加加锁方法不得被访问

//某对象,其中有加锁方法
class Student{
 public synchronized void m1(){
  System.out.println("m1已运行!!!");
  //某个线程在这个方法中时,其他的线程不能访问其他加锁方法
  try {
   Thread.sleep(5000);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }
 public synchronized void m2(){
  System.out.println("m2已运行!!!");
 }
 //不加锁的方法可以访问
 public void m3(){
  System.out.println("m3已运行!!!");
 }
}

4.死锁

两个线程各访问一个对象后,没有及时解锁,然后又互相需要各自的对象时,都不让步造成的

  • 代码块锁
  • synchronized(xxx){} 代码块锁,可以锁类,也可以锁对象
  • 如果锁对象,当访问该代码块锁的时候,该对象中所有的代码块锁和加锁的成员方法都被锁定
  • 同理 访问对象中加锁的成员方法的时候,代码块锁也会被锁定
  • 如果是锁类,当访问该代码块的时候,该类中所有的代码块锁和加锁的静态方法都被锁定
  • 同理 访问类中加锁的静态方法的时候,代码块锁也会被锁定
public void run() {
  //访问o1对象,并加锁
  synchronized (o1){
   try {
    Thread.sleep(100);
   } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   //访问o2对象,并加锁
   synchronized (o2){
    System.out.println("t1被执行了");
   }
  }
 } 

5.挂起/唤醒

当一个加锁的方法被访问时,此类中其他的加锁方法也不能被访问,除非被挂起wait(),然后被其他在这个对象上运行的线程唤醒this.notifyAll();后才可以执行

public synchronized void printodd(){
  while (count % 2 == 1) {
   System.out.println(Thread.currentThread().getName()+" :"+count);
   count++;
   this.notifyAll();
   try {
    Thread.sleep(500);
    this.wait();
   } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
 }
 public synchronized void printeven(){
  while (count % 2 == 0){
   System.out.println(Thread.currentThread().getName()+" :"+count);
   count++;
   this.notifyAll();
   try {
    Thread.sleep(500);
    this.wait();
   } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
 }

6.线程池

线程池的作用 :

  • 线程池作用就是限制系统中执行线程的数量
  • 根据系统的环境情况,可以自动或者手动来设置线程数量,以达到运行的最佳效果
  • 少了浪费系统资源,多了造成系统拥挤效率不高
  • 用线程池控制线程数量,其他线程排队等候
  • 一个任务 执行完成,再从队列中取最前面的任务开始执行
  • 如果队列中没有等待进程,线程池的这个资源处于等待状态
  • 当一个新任务需要运行时,如果此时线程池中还有等待中的工作线程时,可以直接开始运行
  • 否则需要进入等待队列
  • 为什么要使用线程池
  • 1 减少了创建 和销毁线程的次数,因为每个工作线程都可以被重复使用,可执行多个任务
  • 2 可以根据系统的承受能力,调整线程池中的线程数量,防止因为消耗过多的内存,导致服务器死机
  • (每个线程需要大概1MB内存,线程开的越多,消耗内存越大,最后导致死机)
  • 使用线程池代码
public static void main(String[] args) {
  //创建线程池,可缓存,可以自动回收空闲线程,池子规模不限制,数量不固定
  ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
  for (int i = 0; i < 10; i++) {
   cachedThreadPool.execute(new Processor_07());
  }
  System.out.println(Thread.currentThread().getName()+"++++++++++++++++");
  //关闭线程池
  cachedThreadPool.shutdown();
 }

创建一个固定 长度线程池,可控制线程最大并发数
超出此数量的线程,会在队列中等待
可以规定线程数量,这些线程在池子中循环使用

  • 使用固定线程池代码
public static void main(String[] args) {
  ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
  for (int i = 0; i < 10; i++) {
   fixedThreadPool.execute(new Processor_08());
  }
  System.out.println(Thread.currentThread().getName()+"++++++++++++++++");
  //关闭线程池
  fixedThreadPool.shutdown();
 }
  • 周期\定时\常量池

创建一个固定长度线程池,支持定时及周期性执行任务

 public static void main(String[] args) {
  //创建定时周期执行任务常量池
  ScheduledExecutorService scheduledThreadPool = Executors
    .newScheduledThreadPool(5);
  //延迟5秒执行
//  scheduledThreadPool.schedule(new processor_08_(), 5,TimeUnit.SECONDS);
  //延迟5秒执行,并间隔一秒
  scheduledThreadPool.scheduleAtFixedRate(new processor_09_(), 5, 1, TimeUnit.SECONDS);
 }

单线程线程池,只创建一个线程,如果这个 线程因为异常结束,那么会有一个新的线程来替代他

  • 该线程保证所有的任务的执行顺序,按照任务的提交顺序执行,谁先来谁先执行
  • 适用于一个一个任务执行的情况
  • 单线程常量池代码
ExecutorService singleThreadExecutor = Executors
    .newSingleThreadExecutor();
  // 一次性创建10个请求
  for (int i = 0; i < 10; i++) {
   // 只能依次执行
   singleThreadExecutor.execute(new Runnable() {
    @Override
    public void run() {
     try {
      Thread.sleep(1000);
      System.out.println(Thread.currentThread().getName()
        + " : " + 1);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
    }
   });
  }

7.懒汉优化(多线程)

  • 懒汉式单例模式优化
//直接锁住方法效率会降低,因为有些已创建对象的线程原本不需要等待即可得到返回值
// public synchronized static singleLethon creat(){
//  if (sl == null) {
//   sl = new singleLethon();
//  }
//  return sl;
//  
// }
 //再次优化
 public static singleLethon creat(){
  //此判断是:判断是否为已创建对象,否的话直接返回
  if (sl == null) {
   synchronized(singleLethon.class){
    //此判断是:判断已经进入外循环等待的线程应该是都没有创建对象
    //但是一旦把锁放开就会给等待的线程都创建新的对象
    //所以二次判断:判断如果已经有对象就不再创建
    if (sl == null) {
     sl = new singleLethon();
    }
   }
  }
  return sl;
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值