目录
b.等待一段时间线程被唤醒就继续执行,否则不再继续等待,继续执行
wait和notify是Object类的方法,用于线程的等待与唤醒,必须搭配synchronized锁来使用
进入此方法的线程会进入阻塞态(WATING),直到有其他线程调用notify方法唤醒此线程
- public void run() {
- synchronized (lock) {
- System.out.println(Thread.currentThread().getName()+"此线程准备进入等待方法");
- try {
- //等待其他线程将此线程唤醒
- lock.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- public void run() {
- synchronized (lock) {
- System.out.println(Thread.currentThread().getName()+"此线程准备进入等待方法");
- try {
- //等一秒,你不唤醒我我就继续了
- lock.wait(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- public void run() {
- synchronized (lock) {
- System.out.println(Thread.currentThread().getName()+"此线程准备唤醒其他线程");
- lock.notify();
- System.out.println("唤醒结束");
- }
- }
- public void run() {
- synchronized (lock) {
- System.out.println(Thread.currentThread().getName()+"此线程准备唤醒其他线程");
- lock.notifyAll();
- System.out.println("唤醒结束");
- }
- }
对于wait和notify方法,不仅存在阻塞队列,还存在等待队列
当我们线程进入wait方法之前是要获取锁的,获取锁的线程调用.wait方法会进入等待队列,并且将锁释放,其余线程在阻塞队列中等待获取锁。

调用notify方法时,会将等待队列的线程唤醒,并将这个线程送回到阻塞队列,再次获取锁对象来进行线程wait之后的操作。

1.wait方法是Object类提供的方法,需要搭配synchronized锁来使用,调用wait方法会释放锁,线程进入WAITING状态,等待被其他线程唤醒或着超时自动唤醒,唤醒之后的锁需要再次竞争synchronized锁才能继续执行。
2.sleep方法是Thread类提供的方法,调用sleep方法的线程进入TIMED_WAITING状态,不会释放锁,时间到自动唤醒。
Thread.activeCount()//包括一个隐藏的后台线程
单例模式就是保证类在程序中只有一个唯一的对象
要创建类的对象,我们就要用到类的构造方法,所以我们将构造方法私有化,就无法在类的外部产生对象了。
- public class SingleTon {
- private SingleTon() {};
- }
1.构造私有化(保证对象的产生个数)
2.单例类的内部提供这个唯一对象(static)
3.单例类提供返回这个唯一的对象的静态方法供外部使用
饿汉式单例采用直接在类中创建唯一静态对象,保证在JVM加载类的过程中创建这个唯一对象
不管是否产生此类对象,只要类加载,唯一对象就会产生。
所以,饿汉式单例的创建也满足天然线程安全,对象只在类加载时产生一次
我们可以创建get方法来获取这个唯一的对象。
- public class SingleTon {
- //产生唯一对象
- public static SingleTon singleTon = new SingleTon();
- private SingleTon() {};
- //可通过方法获取此对象
- public static SingleTon getSingleTon() {
- return singleTon;
- }
- }
当第一次调用getSingleTon方法,表示外部需要获取这个单例对象时才产生对象
在系统初始化时,外部并不需要这个单例对象,就先不产生,只有当外部需要此对象才实例化对象。
这种操作称之为懒加载
我们会用条件判断的方式来决定是否产生对象,而当多线程并行执行时,每条线程都产生了一个对象,结果就不对了
- public class LazySingleTon {
- private static LazySingleTon lazySingleTon;
- private LazySingleTon(){}
- public static LazySingleTon getLazySingleTon() {
- //第一次调用时产生对象
- if(lazySingleTon==null) {
- lazySingleTon=new LazySingleTon();
- }
- return lazySingleTon;
- }
- }
首先,当多线程出现线程安全问题时,最好的方式就是加锁,如果我们直接加在方法上,当然可以,但是锁的粒度太粗,体现不出多线程的快速。
因此,我们需要优化方法锁
尝试优化:
首先,我们尝试在条件内部加锁:

这样加锁,仍然无法阻止其他线程判断到对象未创建这一步,所有线程都会进到已经判断为null之后再进行加锁操作,这样没有意义。
再次进行尝试,在锁的内部再次判断对象是否创建完成:

进行到这一步时, 逻辑上已经没问题了,但是会出现这样一种问题:
线程在创建对象的过程中,其他线程获取开始在外层条件判断对象是否已经创建,

此时,虽然对象还未创建完成,但是外面已经显示不为空了,这样就会导致其他线程把没有创建好的对象获取。
我们可以在对象处加上volatile关键字,保证当创建完对象后,才可进行return方法。

这种解决懒汉式单例线程安全问题的步骤,我们称为double-check。
阻塞队列也遵守先进先出的原则。
队列满时,继续入队列就会阻塞,直到有其他线程从队列取走元素。
队列空时,继续出队列就会阻塞,直到有其他线程往队列插入元素。
当收到大量支付请求时,我们可以把这些支付请求放入缓冲区,也就是一个阻塞队列中,慢慢处理请求。
入队方法 put()
出队方法 take()
ArrayBlockingQueue
LinkedBlockingQueue


JDK中使用Timer类描述定时器,核心方法就是schedule(指定时间到了要执行的任务,等待时间-ms)
- public static void main(String[] args) {
- Timer timer = new Timer();
- timer.schedule(new TimerTask() {
- @Override
- public void run() {
- System.out.println("hello timer");
- }
- //延时3秒开始,每1s执行一次
- },3000,1000);
- }
可设置要执行的任务延迟多久开始,每隔多久执行一次