java学习day10

线程锁对象

锁对象Lock

创建锁对象

        可重入锁

        

        公平锁

        

方法

lock.lock();

加锁

lock.trylock();

尝试加锁,加锁成功返回true,失败返回false

lock.unlock();

解锁

读写锁

ReentrantReadWriteLock是锁的实现类,内部维护了两个锁,一个读锁,一个写锁

获取读锁

获取写锁

写锁是独占的,读锁是共享的

在使用synchronized代码块时在括号内需要锁对象

代码块内锁对象可以使用方法

以OBJ作为代码块的锁对象

OBJ.notify()

唤醒一条被该对象wait的线程

OBJ.notifyAll()

唤醒全部被该锁对象wait的线程

OBJ.wait()

让执行到该行代码的线程进入等待状态(等待池)

线程池

池即重用

线程池完成线程的创建和管理,销毁工作

定义

        qu定义                                

        ThreadPoolExecutor的构造方法共有七个参数

int corePoolSize

核心进程数

int maximumPoolSize

最大进程数

long keepAliveTime

存活时间

TimeUnit unit

时间单位

BlockingQueue<Runnable> workQueue

工作队列

ThreadFactory threadFactory

线程工厂

RejectedExecutionHandler handler

回拒策略

线程任务可以有两种方式设置

Runnable

Callable

其中使用Runnable方法也可以使用submit方法设置

submit方法可以返回Feature<V>类型获得线程返回的信息

f.get()方法可以在等待线程执行完毕后获取线程返回的信息

线程池的四种回绝策略

1.AbortPolicy() 放弃该任务并会抛出一个异常RejectedExecutionException

2.CallerRunsPolicy() 调用者执行,让传递任务的线程执行任务

3.DiscardOldestPolicy() 放弃队列当中时间最长的任务,不会抛出异常

4.DiscardPolicy() 直接放弃新的任务,不会抛出异常

线程池的四种内置线程池

  1. Executors.newCachedThreadPool();

        java中内置的线程池对象

        可以根据工作任务来创建线程,如果没有空闲的线程就创建新的线程,线程存活时间60s

  1. Executors.newFixedThreadPool(10);

        设定最大线程数量的线程池

  1. Executors.newScheduledThreadPool(10);

        提供定时运行的处理方案

  1. Executors.newSingleThreadExecutor();

        创建一个具有单个线程的线程池,保障任务队列完全按照顺序执行

线程池的工作原理

任务放置在工作队列中

  1. 池中是否有空闲的线程,如果有便让该线程执行任务
  2. 如果池中没有空闲的线程,判断线程数量是否达到核心线程数
  3. 如果没有达到,创建新的线程执行任务,直到填满核心线程数。

如果已经达到,优先在队列存储,直到队列填满

  1. 当工作队列填满后,判断池中的线程数量有没有达到最大线程数

没有达到最大线程数,创建新的线程执行任务

直到填满最大线程数

  1. 已经填满最大线程数,队列也已经填满,没有空闲的线程,就执行回绝策略

注意:线程池中的线程达到(超过)核心线程数,超出的数量会根据存活时间,进行销毁。直到数量达到核心线程数。

    如果线程的数量少于核心线程数,不会消亡

单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供了一个全局访问点来访问该实例。

注意:

1、单例类只能有一个实例。

2、单例类必须自己创建自己的唯一实例。

3、单例类必须给所有其他对象提供这一实例。

单例模式的实现方式

1、懒汉式,线程不安全

这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。

这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。

2、懒汉式,线程安全

这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。

优点:第一次调用才初始化,避免内存浪费。

缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。

getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。

3、饿汉式

这种方式比较常用,但容易产生垃圾对象。

优点:没有加锁,执行效率会提高。

缺点:类加载时就初始化,浪费内存。

它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。

4、双检锁/双重校验锁(DCL,即 double-checked locking)

这种方式采用双锁机制,安全且在多线程情况下能保持高性能。

getInstance() 的性能对应用程序很关键。

5、枚举

这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。

这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。

不能通过 reflection attack 来调用私有构造方法。

死锁

死锁是一种非常严重的bug,是说多个线程同时被阻塞,线程中的一个或者多个又或者全部都在等待某个资源被释放,造成线程无限期的阻塞,导致程序不能正常终止

死锁产生的四个必要条件

  1. 互斥使用:当资源被一个线程使用或者占用时,别的线程不能使用该资源
  2. 不可抢占:获取资源的一方,不能从正在使用资源的一方抢占掠夺资源,资源只能被使用者主动释放
  3. 请求和保持:资源请求者在请求别的资源时,同时保持对已有资源的占有
  4. 循环等待:即p1占有p2的资源,p2占有p3的资源,p3占有p1的资源,这样形成了一个等待环路

如何避免死锁

死锁的产生必须满足互斥使用,不可抢占,请求和保持,循环等待这四个条件,但是只要破坏其中任意一个条件即可破坏死锁,其中最容易破坏的就是循环等待这个条件,那么如何破坏循环等待这个条件呢?

多个线程约定好一定的顺序,按照这个顺序加锁释放锁

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值