多线程案例,单例模式,阻塞队列,定时器

重点:单例模式,阻塞队列,定时器,线程池

1.单例模式

1.1定义

  • 是一种设计模式,在代码中有些对象应该只有一个实例,就称为“单例”
  • eg:数据库的DataSource应该就是一个单例
  • 有些负责加载数据到内存的类也应该是单例
  • 强制性某个类只有一个实例

1.2实现

  • 主要依托于static关键字,静态成员

1.2.1饿汉模式

/**
 * User:yang
 */
public class ThreadDemo16 {
    //饿汉模式
    //实例创建出现在类加载阶段
    static class Singleton {
        //希望是单例类,只有一个实例

        //县创建一个成员,保存唯一的实例
        private static Singleton instanse = new Singleton();

        //在提供方法获取实例
        public static Singleton getInstance() {
            return instanse;
        }

        //把构造方法私有,防止其他代码创建实例
        private Singleton() {
        }
    }

    public static void main(String[] args) {
//如何获取这个实力
        Singleton singleton=Singleton.getInstance();
    }
}

1.2.2懒汉模式

/**
 * User:yang
 */
public class ThreadDemo17 {
    //懒汉模式
    //创建实例是在第一次使用getinstance的时候,比饿汉模式迟
    static class Singleton {
        private static Singleton instance = null;

        public static Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }

        private Singleton() {
        }
    }

    public static void main(String[] args) {
        Singleton singleton=Singleton.getInstance();
    }
}

1.3如何保证懒汉模式线程安全

  • 加锁
  • 以效率为代价保证安全
/**
 * User:yang
 */
public class ThreadDemo18 {
    //线程安全版本懒汉模式
    //创建实例是在第一次使用getinstance的时候,比饿汉模式迟
    static class Singleton {
        private static Singleton instance = null;

         public static Singleton getInstance() {
          synchronized (Singleton.class){
              if (instance == null) {
                  instance = new Singleton();
              }
          }
            return instance;
        }

        private Singleton() {
        }
    }

    public static void main(String[] args) {
        Singleton singleton=Singleton.getInstance();
    }
}

  • 优化,当已经创建实例后,不进入加锁,并且volatile防止寄存器优化
    *
/**
 * User:yang
 */
public class ThreadDemo18 {
    //线程安全版本懒汉模式
    //创建实例是在第一次使用getinstance的时候,比饿汉模式迟
    static class Singleton {
        private static Singleton instance = null;

        public static Singleton getInstance() {

            if (instance == null) {
                synchronized (Singleton.class) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }

        private Singleton() {
        }
    }

    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
    }
}

2.阻塞队列

2.1定义

  • 1.这个队列是线程安全的(内部进行了加锁控制)
  • 2.当队列满的时候,往里插入元素,此时就会阻塞,一直阻塞到队列不满的时候插入
  • 当队列为空的时候,从队列取元素也会阻塞,一直阻塞到队列不为空的时候才完成取元素
  • 使用场景:生产着消费者模型

2.1.1生产着消费者模型

  • 实例:
  • 包饺子
  • 擀饺子皮(生产者)
  • 包(消费者)
  • 在服务器开发中很有用,一个服务器,同一时刻可能受到很多请求,服务器能力有限,可能会挂(削弱峰值)
  • 实际上我们会在请求的时候使用阻塞队列,应用程序按照固定节奏从阻塞队列里面获取数据,请求在阻塞队列缓存着,不会消耗太多CPU资源
  • 消息队列:也是阻塞队列,但是功能更强大
  • 1.里面的数据带有类型topic,按照topic进行分类,把相同的topic的数据放到不同的队伍当中,分别进行排队,
  • 一个消息队列,可以同时支撑多个业务的多组数据
  • 2.往往是单独的服务器/服务器集群,通过网络通信的方式,进行“生产/消费”
  • 3.还支持持久化存储(数据存在磁盘上)
  • 4.消费的时候支持多种消费模式
  • (1.指定位置消费,(不一定是取出队首元素)
  • (2.镜像模式消费(一个数据可以被取多次,不是取一次就直接删除)

3.Java提供的BlockingQueue

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * User:yang
 */
public class ThreadDemo20 {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> queue = new LinkedBlockingQueue<>(10);
        //创建生产者线程
        Thread producer = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    try {
                        System.out.println("producer生产  str" + i);
                        queue.put("str" + i);
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        producer.start();
//消费者线程
        Thread customer = new Thread() {
            @Override
            public void run() {

                while (true) {
                    try {
                        String elem = queue.take();
                    Thread.sleep(1000);
                        System.out.println("customer获取到" + elem);
                    } catch (InterruptedException e) {
                        e.printStackTrace();

                    }
                }
            }
        };
        customer.start();
        try {
            producer.join();
            customer.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

4.自己实现阻塞队列

4.1基于数组(环形队列)

/**
 * User:yang
 */
public class ThreadDemo21 {
    //先基于数组的方式实现一个普通队列
    //再改进程阻塞队列
    static class BlockingQueue {
        private int[] items = new int[1000];
        //约定[head,tail)是一个前闭后开区间
        //初始状态head,tail为0,表示区间为空
        //从head取元素
        private int head = 0;
        //往tail添加元素
        private int tail = 0;
        //表示元素个数
        public int size = 0;
        //引入一个“锁对象”
        private Object locker = new Object();

        //入队列
        public void put(int value) throws InterruptedException {
            synchronized (locker) {
                while (size == items.length) {//wait结束之后,不一定队列不是满的
                    //队列已经满了,阻塞等待
                    locker.wait();
                }
                //队列没有满,插入到tail位置
                items[tail] = value;
                tail++;
                //处理tail超出边界
                if (tail >= items.length) {
                    tail = 0;
                }
                size++;
                locker.notifyAll();
            }

        }

        public Integer take() throws InterruptedException {
            int ret = 0;
            synchronized (locker) {
                //队列为空,阻塞等待
                while (size == 0) {
                    locker.wait();
                }
                ret = items[head];
                head++;
                if (head >= items.length) {
                    head = 0;
                }
                size--;
                locker.notifyAll();
            }
            return ret;

        }

    }

    public static void main(String[] args) throws InterruptedException {
        BlockingQueue queue = new BlockingQueue();
        Thread producer = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    try {
                        System.out.println("产生了元素" + i);
                        queue.put(i);
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        producer.start();
        Thread customer = new Thread() {
            @Override
            public void run() {
                 while (true){
                     int ret= 0;
                     try {
                         ret = queue.take();
                         System.out.println("消费了元素"+ret);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                 }
            }
        };
        customer.start();
producer.join();
customer.join();
    }

    public static void main1(String[] args) throws InterruptedException {
        BlockingQueue queue = new BlockingQueue();
        queue.put(1);
        queue.put(2);
        queue.put(3);
        queue.put(4);
        System.out.println(queue.take());
        System.out.println(queue.take());
        System.out.println(queue.take());
        System.out.println(queue.take());
        System.out.println(queue.take());


    }
}


4.2基于链表

5.定时器

  • 设置一个任务,约定这个任务在指定时间之后执行
  • Timer提供核心及入口,schedule(安排)指定一个任务交给定时器,在一定的时间之后来执行这个任务。
  • 1.Timer中要包含一个Task类,每个Task就表示一个具体的任务实例
  • Task里面包含一个时间戳(啥时候执行任务),还包含一个Runnable实例(用来表示任务具体是啥)
  • 2.Timer里面通过一个带优先级的阻塞队列(时间到了就先执行,快要到时间的任务优先级更高),来组织若干个task
  • 3.Timer中还需要一个专门的线程,让这个线程不停的扫描队首元素,看看队首元素是不是可以执行了,如果可以执行了,就执行这个任务,如果不能执行,就继续在队列中等待。
import java.util.PriorityQueue;
import java.util.concurrent.PriorityBlockingQueue;

/**
 * User:yang
 * 一个简单定时器
 */
public class ThreadDemo22 {
    //每个Task就表示一个具体的任务实例
    //task放入优先队列的时候要比较优先级
    static class Task implements Comparable<Task> {
        //啥时候执行
        private long time;
        //执行啥
        private Runnable command;

        //
        public Task(Runnable command, long time) {
            this.command = command;
            //绝对时间
            this.time = System.currentTimeMillis() + time;
        }

        public void run() {
            command.run();
        }

        @Override
        public int compareTo(Task o) {
            //时间小的排前面
            return (int) (this.time - o.time);
        }
    }


    static class Timer {
        //创建一个带优先级的阻塞队列
        private PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();
        //        用这个对象来完成线程之间的协调任务
        private Object mailbox = new Object();

        //schedule方法就是把一个task放到Timer中
        public void schedule(Runnable command, long after) {
            Task task = new Task(command, after);
            queue.put(task);
            synchronized (mailbox) {
                mailbox.notify();
            }
        }

        public Timer() {
            Thread worker = new Thread() {
                @Override
                public void run() {
                    while (true) {
                        //去队首元素,看能不能执行
                        try {
                            Task task = queue.take();
                            long currentTime = System.currentTimeMillis();
                            if (currentTime >= task.time) {
                                //执行任务
                                task.run();
                            } else {
                                //时间没到继续等
                                queue.put(task);
                                synchronized (mailbox) {
                                    mailbox.wait(task.time - currentTime);
                                }
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                    }
                }
            };
            worker.start();
        }

    }


    public static void main(String[] args) {
Timer timer=new Timer();
Runnable comand=new Runnable() {
    @Override
    public void run() {
        System.out.println("时间到了");
        timer.schedule(this,3000);
    }
};
        System.out.println("安排任务");
timer.schedule(comand,3000);
    }
}

5.5 定时器总结

  • 目的:让某个任务在某个时间点再执行,不是立即执行
  • 接口:schedule把一个任务和时间加入到定时器中
  • 结构:
  • (1)Task类,来描述一个任务
  • (2)带优先级的阻塞队列
  • (3)线程,扫描队首元素
  • (4)mailBox防止扫描线程忙等

6.协程

  • 比线程更升级

7.线程池(java提供的)

  • 把一些线程创建好,用的时候从池子取一个线程,用完了不是销毁,而是放回池子里。
  • java提供的
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * User:yang
 */
public class ThreadDemo23 {
    public static void main(String[] args) {
        //创建包含10个线程的线程池
        ExecutorService pool= Executors.newFixedThreadPool(10);
//        //创建线程数量动态变化的的线程池
//        ExecutorService pool2=Executors.newCachedThreadPool();
        int i = 0;
        for (; i <100 ; i++) {
            int finalI = i;
            pool.execute(new Runnable() {

                @Override
                public void run() {
                    System.out.println("hello"+ finalI);
                }
            });
        }

    }
}

8.线程池(自己实现)

  • 线程池内部结构
  • 1.描述一个任务(runnable),只需要知道任务做啥,不需要知道任务啥时候执行
  • 2.组织很多任务,使用阻塞队列来保存当前的所有任务
  • 3.有一些线程,来负责执行阻塞队列中的任务,阻塞队列为空则等待
  • 4.还需要list把当前线程保存起来,方便管理
import javafx.concurrent.Worker;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * User:yang
 */
public class ThreadDemo24 {
    static class ThreadPool {
        //描述一个任务(runnable)
        //组织很多任务,使用阻塞队列来保存当前的所有任务
        private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();

        //描述一个线程,用来进行工作
        static class worker extends Thread {
            private BlockingQueue<Runnable> queue=null;
            public worker(BlockingQueue<Runnable> queue){
                this.queue=queue;
            }
            @Override
            public void run() {
                while (true) {
                    try {
                        Runnable runnable =queue.take();
                        runnable.run();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        //八线程组织起来
        private List<worker> workers=new ArrayList<>();
        private static final int maxcount=10;
        //核心接口execute
        public void execute(Runnable command) throws InterruptedException {
            if (workers.size()<maxcount){
                //当前池子没有足够的线程,创建新的线程
                worker worker=new worker(queue);
                worker.start();
                workers.add(worker);
            }
            queue.put(command);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadPool pool=new ThreadPool();
        for (int i = 0; i < 10; i++) {
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello");
                }
            });

        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值