首页 > 其他分享 >多线程总结2(多线程代码案例)

多线程总结2(多线程代码案例)

时间:2023-08-11 20:31:47浏览次数:55  
标签:代码 System 案例 void println new 多线程 public out

1.单例模式(Singleton pattern))

单例模式保证某个类在进程中只创建一个实例

1.1饿汉模式

类加载的同时立即创建实例

class SingleHungry {
    //只创建了这一个唯一的实例
    private static SingleHungry instance = new SingleHungry();
    public static SingleHungry getInstance() {
        return instance;   //饿汉模式只进行读操作,并未修改
    }
    //使用private修饰禁止外部new一个实例
    private SingleHungry() {};
}

public class TheadDemo {
    public static void main(String[] args) {
        SingleHungry singleHungry = SingleHungry.getInstance();
    }
}

1.2懒汉模式

等到第一次使用的时候再开始创建一个实例

public class SingleLazy {
    //new可能引起指令重排序使用volatil修饰
    volatile public static SingleLazy instance = null;
    public static SingleLazy getInstance() {
        //这一个if()判断第一个线程创建完实例实后,保证后面的线程就不需要进行重复加锁操作
        if(instance == null) {
            synchronized(SingleLazy.class) {
                if(instance == null) {
                    instance = new SingleLazy();
                }
            }
        }
        return instance;
    }
    private SingleLazy() {};   //使用private修饰禁止外部new一个实例

    public static void main(String[] args) {
        SingleLazy s1 = SingleLazy.getInstance();
        SingleLazy s2 = SingleLazy.getInstance();
        //是相同的一个
        System.out.println(s1 == s2);
    }
}

多线程总结2(多线程代码案例)_多进程

小概率情况可能出现指令重排序

多线程总结2(多线程代码案例)_多进程_02

2.阻塞队列(BlockingQueue)

阻塞队列是一种特殊的队列,使得线程安全的一种数据结构

也遵循"先进先出"原则

特性:

当列队满的时候,再次压入队列,就会进行阻塞等待,直到其他线程从列队中取走取走元素

而当列队为空时,再次弹出列队,就会进行阻塞等待,直到其他线程从列对中存入元素.

2.1阻塞队列的简单使用

代码实现

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

public class ThreadDemo2 {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>();
        //阻塞列队使用put方法进行入对操作
        blockingQueue.put("1");
        blockingQueue.put("2");
        blockingQueue.put("3");
        blockingQueue.put("4");
        String res = null;
        //阻塞列队使用take方法进行出队操作
        res = blockingQueue.take();
        System.out.println(res);
        res = blockingQueue.take();
        System.out.println(res);
        res = blockingQueue.take();
        System.out.println(res);
        res = blockingQueue.take();
        System.out.println(res);
        res = blockingQueue.take();
        System.out.println(res);
        

    }
}

结果显示

多线程总结2(多线程代码案例)_多进程_03

再多弹出一次看看

String res = null;
        //阻塞列队使用take方法进行出队操作
        res = blockingQueue.take();
        System.out.println(res);
        res = blockingQueue.take();
        System.out.println(res);
        res = blockingQueue.take();
        System.out.println(res);
        res = blockingQueue.take();
        System.out.println(res);
        res = blockingQueue.take();
        System.out.println(res);

多线程总结2(多线程代码案例)_多进程_04

并未结束发现进入阻塞等待状态

2.2使用阻塞队列编写生产者消费者模型

为什么会有这种模型?

首先我们理解一下耦合 内聚的概念

两个模块之间关联越强,耦合越高,反之耦合越低;(代码之间追求低耦合,防止之间相互影响)

相关联的代码无序摆放,低内聚; 相关联的代码归类的摆放,高内聚;

1.使得上下模块之间"解耦合"(高内聚,低耦合);

多线程总结2(多线程代码案例)_多进程_05

多线程总结2(多线程代码案例)_多进程_06

2.消峰填谷

多线程总结2(多线程代码案例)_多进程_07

某些时候服务器1的请求比平时好几倍的激增, 如果没有阻塞队列缓冲,就可能导致服务器2超出峰值直接跪掉了

使用了队列,当服务器1激增的时候,队列的数据也会增,

就会帮助服务器2 以之前的速率获取数据

保障了系统的稳定性

代码简单实现

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

public class blockingQueue {
    public static void main(String[] args) {
        BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
        Thread consumer = new Thread(() -> {
            while (true) {
                try {
                    int val = queue.take();
                    System.out.println("消费"+val);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        consumer.start();
        Thread producer = new Thread(() -> {
            int val = 0;
            while (true) {
                try {
                    System.out.println("生产"+val);
                    queue.put(val);
                    val++;
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        producer.start();
    }
}

结果显示

多线程总结2(多线程代码案例)_多进程_08

2.3自己创建一个阻塞队列实现"消费者生产者"模型(*)

1.实现一个队列

2.加锁(synchronized关键字)

3.加上阻塞功能(wait,notify)

如何区分队列空或满

1.舍弃一个空间区分

2.记录元素个数

代码实现

public class MyBlockingQueue {
    private int[] items = new int[500];
    volatile int head = 0;
    volatile int tail = 0;
    //记录元素个数
    volatile int size = 0;
    //入队列
    synchronized public void put(int val) throws InterruptedException {
        //判断是否为队列满
        if(size == items.length) {
            //为满等待其他线程取走元素
            this.wait();
        }
        //采用尾加
        items[tail] = val;
        tail++;
        //采用循环队列,tail达到尾巴,重头开始
        if(tail == items.length) {
            tail = 0;
        }
        //或者使用取模tail = tail % items.length;
        size++;
        就存入了一个元素,可以通知上次为空的结束等待
        this.notify();
    }
    出队列
    synchronized public int take() throws InterruptedException {
         //判断是否为队列空
        if(size == 0) {
            //为空等待其他线程填入元素
            this.wait();
        }
        int val = items[head];
        head++;
        if(head == items.length) {
            head = 0;
        }
        size--;
        //就取走了一个元素,可以通知上次为满的结束等待
        this.notify();
        return val;
    }
    public static void main(String[] args) {
        MyBlockingQueue queue = new MyBlockingQueue();
        Thread t1 = new Thread(() -> {
            while (true) {
                try {
                   int val =  queue.take();
                    System.out.println("消费"+val);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t1.start();
        Thread t2 = new Thread(() -> {
            int val = 0;
           while (true) {
               try {
                   System.out.println("生产" + val);
                   queue.put(val);
                   val++;
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
        });
        t2.start();

    }
}

多线程总结2(多线程代码案例)_多进程_09

当wait的等待条件还不满足时

wait可能被外部interrupt方法唤醒

这样我们就可以等待wait唤醒后在判定一定条件  如果不满足继续wait;

结果显示

多线程总结2(多线程代码案例)_多进程_10

3.定时器

构造方法

多线程总结2(多线程代码案例)_多进程_11

普通方法

多线程总结2(多线程代码案例)_多进程_12

我们从官方文档可以了解到TimerTask 实现了Runnable接口需要重新run方法

多线程总结2(多线程代码案例)_多进程_13

java-api的中文文档有需要的小伙伴自取

java api

3.1有关定时器的简单使用

import java.util.Timer;
import java.util.TimerTask;

public class Time {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("good!");
            }
        },4000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("so");
            }
        },3000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("are");
            }
        },2000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("You");
            }
        }, 1000);
    }
}

多线程总结2(多线程代码案例)_多进程_14

结果显示


3.2自己实现一个简单定时器(*)

多线程总结2(多线程代码案例)_多进程_15

代码实现

import java.util.PriorityQueue;
class MyTask implements Comparable<MyTask>{
    //我们可以从官方文档可以看到Runnable不仅是接口,还可以作为一个类使用
    public Runnable runnable;
    //毫秒时间戳
    long time;
    public MyTask(Runnable runnable, long time) {
        this.runnable = runnable;
        //初始化当前系统的时间加上要运行的时间
        this.time = System.currentTimeMillis() + time;
    }
    //要注意实现任务之间的比较方法,这里比较的是最小时间
    @Override
    public int compareTo(MyTask o) {
        return (int) (this.time - o.time);
    }
}
class MyTimer {
    //定时器的构成:一个带优先级的阻塞队列
    PriorityQueue<MyTask> queue = new PriorityQueue<>();
    //创建一个锁对象
    private Object block = new Object();
    public void schedule(Runnable runnable, long time) {
        synchronized (block) {
            MyTask myTask = new MyTask(runnable, time);
            queue.offer(myTask);
            block.notify();
        }
    }
    //这里构造线程,负责实现具体的任务
    public MyTimer() {
        Thread t1 = new Thread(() -> {
            while (true) {
                synchronized (block) {
                    while (queue.isEmpty()) {
                        try {
                            block.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    MyTask myTask = queue.peek();
                    long curTime = System.currentTimeMillis();
                    //到时间就可以执行run方法了
                    if(curTime >= myTask.time) {
                        queue.poll();
                        myTask.runnable.run();
                    } else {
                        try {
                            //时间没得,等待剩余的时间
                            block.wait(myTask.time - curTime);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        });
        t1.start();
    }
}
public class ThreadDemo {
    public static void main(String[] args) {
        MyTimer timer = new MyTimer();
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("good!");
            }
        },4000);
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("so");
            }
        },3000);

        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("are");
            }
        },2000);

        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("you");
            }
        },1000);

    }
}

结果显示

多线程总结2(多线程代码案例)_多进程_16

4.线程池

构造方法

多线程总结2(多线程代码案例)_多进程_17

多线程总结2(多线程代码案例)_多进程_18

如果当前的任务数过多,线程池就会创建一些临时线程

标准库提供的四种拒绝策略

多线程总结2(多线程代码案例)_多进程_19

4.1线性池的简单使用

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

public class ThreadDemo1 {
    public static void main(String[] args) {
        //创建一个线程池,线程的数量为三
        ExecutorService pool = Executors.newFixedThreadPool(3);
        //把任务放到线性池的队列里
        pool.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("you are so good");
            }
        });
    }
}

多线程总结2(多线程代码案例)_多进程_20

4.2自己实现一个线性池

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

class MyThreadPool {
    //使用阻塞队列存放任务
    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue();
   //把任务放到线性池的队列里
    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }
    public MyThreadPool(int n) {
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(() -> {
                try {
                    while (true) {
                        Runnable runnable = queue.take();
                        runnable.run();
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
            t.start();
        }
    }
}
public class ThreadDemo2 {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool pool = new MyThreadPool(20);
        for (int i = 0; i < 600; i++) {
            int n = i;
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("n=" + n);
                }
            });
        }
    }
}




















标签:代码,System,案例,void,println,new,多线程,public,out
From: https://blog.51cto.com/u_16166203/7052237

相关文章

  • 代码随想录算法训练营第十五天| 层序遍历 10 ,226.翻转二叉树 101.对称二叉树 2
     层序遍历   卡哥建议:看完本篇可以一口气刷十道题,试一试, 层序遍历并不难,大家可以很快刷了十道题。  题目链接/文章讲解/视频讲解:https://programmercarl.com/0102.%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%B1%82%E5%BA%8F%E9%81%8D%E5%8E%86.html  做题思......
  • 代码随笔-某游戏网站数据的爬取
    importrequestsimportparselimportcsvimportre#将表头写入CSV文件withopen('xxxgame.csv',mode='a',encoding='utf-8-sig',newline='')asf:csv_writer=csv.DictWriter(f,fieldnames=['title','nu......
  • 多线程开发 使用Semaphore和BoundedSemaphore对象
    数据库mportthreadingimporttimedeffunc(semaphore:threading.Semaphore,num):#获得信号量,信号量-1semaphore.acquire()print(f"打印信号量:第{num}次")time.sleep(3)#释放信号量,信号量+1semaphore.release()if__name__=='__ma......
  • 王道408---冒泡排序、快速排序、直接插入排序、希尔排序、二路归并排序、简单选择排序
    一、冒泡排序冒泡排序属于交换类的排序//时间复杂度:O(n^2)//空间复杂度:O(1)//稳定排序算法#include<stdio.h>#include<iostream>usingnamespacestd;intarr[16];voiddebug(){for(inti=1;i<16;i++){printf("%d",arr[i]);}puts("......
  • python 常用的案例1
          pythonPython中文转拼音代码(支持全拼和首字母缩写)by Crazyant本文的代码,从https://github.com/cleverdeng/pinyin.py升级得来,针对原文的代码,做了以下升级: 1、可以传入参数firstcode:如果为true,只取汉子的第一个......
  • python案例2
         pythonpython子类调用父类的方法by Crazyantpython和其他面向对象语言类似,每个类可以拥有一个或者多个父类,它们从父类那里继承了属性和方法。如果一个方法在子类的实例中被调用,或者一个属性在子类的实例中被访问,但是该方法或属性在子类中并不存在,那么就会......
  • 代码生成以及数据生成
    我们在正常开发中设计到数据库的设计,以及对应实体类的代码。我现在讲解两个知识点。代码先行以及数据库先行1、代码先行就是你在程序中创建一个类库,专门用来管理你的实体类实体类写完后,利用ORM框架,譬如EF或者SqlSugar自带的性质可以直接生成数据库,以及数据表而代码实体类创......
  • [好文推荐] 如何保护价值上千万的Node.js源代码?
    如何保护价值上千万的Node.js源代码?https://zhuanlan.zhihu.com/p/84386456一个强大的JS混淆器。github.com/javascript-obfuscator/javascript-obfuscator一套JS代码安全问题解决方案。www.jshaman.com一个极简的Node.js字节码编译器。github.com/OsamaAbbas/bytenodencc......
  • 从零开始一起学习SLAM | 理解图优化,一步步带你看懂g2o代码
    理解图优化,一步步带你看懂g2o框架小白:师兄师兄,最近我在看SLAM的优化算法,有种方法叫“图优化”,以前学习算法的时候还有一个优化方法叫“凸优化”,这两个不是一个东西吧?师兄:哈哈,这个问题有意思,虽然它们中文发音一样,但是意思差别大着呢!我们来看看英文表达吧,图优化的英文是graphoptimi......
  • 01-低代码平台介绍
     1.列表引擎简单配置可快速实现列表页数据展现功能,包括数据的录入、数据的处理、按钮功能的触发、其他组件的联动等 ......