单例模式
用来产生一个对象的具体实例
饿汉模式,不能确保单例在什么时候创建
public class Singleton {
private Singleton(){}
private static Singleton uniqueSingleton = new Singleton();
public static Singleton getInstance(){
return uniqueSingleton;
}
}
饿汉模式,静态初始化时就创建了,保证了线程安全
多线程会出现问题,可以在方法上加synchronized,但会大大影响性能
public class Singleton {
private static Singleton uniqueSingleton;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(uniqueSingleton==null){//用getInstance()去实例化对象,并返回这个实例
uniqueSingleton= new Singleton();
}//如果实例不存在就会创建,不需要这个实例就永远不会创建(延迟实例化)
return uniqueSingleton;
}
}
懒汉模式,双重检查枷锁,据说不好
public class Singleton {
private volatile static Singleton unqiueInstance;//voiatile关键字确保,当uniqueInstance被初始化成Singleton实例时,多个线程正确的处理unqiueInstance
private Singleton(){}
public static Singleton getInstance(){
if(unqiueInstance==null){//检查实例,如果不存在就进入同步区
synchronized (Singleton.class) {//只有第一次才彻底执行这一块的代码
if(unqiueInstance==null){//再检查一次,如果仍是null,才创建
unqiueInstance = new Singleton();
}
}
}
return unqiueInstance;
}
}
没有锁,性能优越,延迟加载
public class Singleton {
private Singleton(){}
public static class SingletonHolder{
private static Singleton instance = new Singleton();
}
//getInstance()第一次被调用,Singleton实例才会被创建
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
不变模式一个对象被创建后,内部状态永远不会发生改变
用在:对象创建后,内部状态和数据不再发生变化;对象需要被共享,被多线程频繁访问
实现:去除所有可以修改自身的方法;所有属性设为私有的,加final,确保不可修改;确保没有子类重载;有一个可以创建完整对象的构造函数
public final class Product {//确保无子类
private final String no;//私有属性,不会被其他对象获取
private final String name;//final属性不会被二次赋值
private final double price;
public Product(String no, String name, double price) {//创建对象时,必须赋值,创建后,无法修改
super();
this.no = no;
this.name = name;
this.price = price;
}
public String getNo() {
return no;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
元素的包装类使用不变模式
生产者消费者模式
public final class PCDate {//任务相关的数据
private final int intDate;//数据
public PCDate(int d) {
this.intDate = d;
}
public PCDate(String d){
this.intDate = Integer.valueOf(d);
}
public int getIntDate() {
return intDate;
}
@Override
public String toString() {
return "PCDate [intDate=" + intDate + "]";
}
}
public class Producer implements Runnable{
private volatile boolean isRunning = true;
private BlockingQueue<PCDate> queue;//内存缓冲区
private static AtomicInteger count = new AtomicInteger();//总数,原子操作
private static final int SLEEPTIME = 1000;
public Producer(BlockingQueue<PCDate> queue) {
this.queue = queue;
}
@Override
public void run() {
PCDate date = null;
Random r = new Random();
System.out.println("start productor id="+Thread.currentThread().getId());
try {
while(isRunning){
Thread.sleep(r.nextInt(SLEEPTIME));
date = new PCDate(count.incrementAndGet());//构造任务数据
System.out.println(date+" is put into queue");
if(!queue.offer(date, 2, TimeUnit.SECONDS)){//提交数据到缓冲区
System.err.println("failed to put date:"+date);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
public void stop(){
isRunning = false;
}
}
public class Consumer implements Runnable {
private BlockingQueue<PCDate> queue;//内存缓冲区
private static final int SLEEPTIME = 1000;
public Consumer(BlockingQueue<PCDate> queue) {
this.queue = queue;
}
@Override
public void run() {
System.out.println("start consumer id="+Thread.currentThread().getId());
Random r = new Random();
try {
while(true){
PCDate data = queue.take();//提取任务
if(null !=data){
int re = data.getIntDate()* data.getIntDate();//计算平方
System.out.println(MessageFormat.format("{0}*{1}={2}", data.getIntDate(),data.getIntDate(),re));
}
Thread.sleep(r.nextInt(SLEEPTIME));
}
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
}
停止生产者失败
因为有锁,效率慢,可以使用CAS无锁,但很复杂
无锁的缓存框架Disruptor
内部实现为一个普通的数组,需要默认设置数组大小,提供当前位置cursor,为了快速从一个序列对应到数组的实际位置,每次有元素入队,序列就加1,必须将数组大小设置为2的整数次方,通过sequence&(queueSize-1)能快速定位到实际元素位置,2的幂次方,表示二进制为10,100,1000等,queueSize-1必为全1的数字,可以将sequence限定在queueSize-1范围内,不会有浪费
public class PCDate {//任务相关的数据
private long value;//数据
public long getValue() {
return value;
}
public void setValue(long value) {
this.value = value;
}
}
//构建所有的缓冲区中的对象实例
public class PCDataFactory implements EventFactory<PCDate>{
@Override
public PCDate newInstance() {
return new PCDate();
}
}
public class Producer{
private final RingBuffer<PCDate> ringBuffer;//环形缓冲区的引用
public Producer(RingBuffer<PCDate> ringBuffer) {
this.ringBuffer = ringBuffer;
}
public void pushDate(ByteBuffer bb){//将产生的数据推入缓冲区
long sequence = ringBuffer.next();//得到下一个可用的序列号
try {
PCDate event = ringBuffer.get(sequence);//获得下一个PCDdata
event.setValue(bb.getLong(0));//设为期望值
} finally {
ringBuffer.publish(sequence);//数据发布
}
}
}
//读取数据进行处理
public class Consumer implements WorkHandler<PCDate> {
//读取已经封装
@Override
public void onEvent(PCDate event) throws Exception {
System.out.println(Thread.currentThread().getId()+":Event:--"+event.getValue()*event.getValue()+"--");
}
}
public class Main {
public static void main(String []args) throws InterruptedException{
ExecutorService service = Executors.newCachedThreadPool();
PCDataFactory factory = new PCDataFactory();
int bufferSize = 1024;//设置缓冲区大小为2的幂次方
//创建了disruptor对象
Disruptor<PCDate> disruptor = new Disruptor<>(factory, bufferSize, service,ProducerType.MULTI,new BlockingWaitStrategy());
//设置消费者,将每个消费者映射到一个线程
disruptor.handleEventsWithWorkerPool(new Consumer(),new Consumer(),new Consumer(),new Consumer());
disruptor.start();//启动并初始化
RingBuffer<PCDate> ringBuffer = disruptor.getRingBuffer();
Producer producer = new Producer(ringBuffer);
ByteBuffer bb = ByteBuffer.allocate(8);
for(long l=0;true;l++){//生产者不断向缓冲区存入数据
bb.putLong(0,l);
producer.pushDate(bb);
Thread.sleep(100);
System.out.println("add data"+l);
}
}
}
选择合适的策略提高消费者的响应时间Disruptor提供策略监控缓冲区的信息,由WaitStrategy接口封装
BlockingWaitStrategy是默认的策略,使用锁和条件监控数据和线程唤醒,涉及到线程切换,最节省CPU,高并发下性能最糟
SleepingWaitStrategy:循环中不断等待数据,先自旋,不成功,使用Thread.yield()让出CPU,最终使用LockSupport.partNamos(1)进行线程休眠,确保不占用太多CPU,延迟较高,异步日志
YieldingWaitStrategy:低延迟,消费者线程会不断循环监控缓冲区变化,循环内部,使用Thread.yield()让出CPU给别的线程执行时间最好有多于消费者线程数量的逻辑CPU数量
BusySpinWaitStrategy:死循环,疯狂监控缓冲区变化,会吃掉所有的CPU,物理CPU数量必须大于消费者线程数
CPU Cache的优化,解决伪共享的问题
Future模式
异步调用,无法立即给出数据,但会返回一个契机,可以凭借契约去重新获取数据
public interface Data {
public String getResult();
}
public class FutureData implements Data{
protected RealData realData = null;//FutureData是RealData的包装
protected boolean isReady = false;
public synchronized void setRealData(RealData realData){
if(isReady){
return;
}
this.realData = realData;
isReady = true;
notifyAll();//RealData已被注入,通知getResult
}
public synchronized String getResult(){
while(!isReady){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return realData.result;
}
}
public class RealData implements Data {
protected final String result;
public RealData(String result) {
StringBuffer sb = new StringBuffer();
for(int i=0;i<10;i++){
sb.append(result);
try {
Thread.sleep(100);//休眠。表示处理时间长
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.result = sb.toString();
}
@Override
public String getResult() {
return this.result;
}
}
public class Client {
public Data request(final String queryStr){
final FutureData futureData = new FutureData();
new Thread(){
public void run(){
//放入线程执行
RealData realData = new RealData(queryStr);
futureData.setRealData(realData);
}
}.start();
return futureData;
}
}
public class FutureMain {
public static void main(String[] args) {
Client client = new Client();
Data data = client.request("name");//因为是futureData,立即返回
System.out.println("请求完毕");
try {
Thread.sleep(2000);//这里作为其他业务处理
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("数据="+data.getResult());//返回数据
}
}
JDK中的Future模式Future接口类似于契约,run方法用于构建真实的数据,call方法返回需要构造的实际数据
public class RealData implements Callable<String> {
private String result;
public RealData(String result) {
this.result = result;
}
@Override
public String call() throws Exception {
StringBuffer sb = new StringBuffer();
for(int i=0;i<10;i++){
sb.append(result);
try {
Thread.sleep(100);//休眠。表示处理时间长
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return sb.toString();
}
}
public class FutureMain {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//构造FutureTask
FutureTask<String> future = new FutureTask<>(new RealData("a"));
ExecutorService eService = Executors.newFixedThreadPool(1);
eService.submit(future);//执行FutureTask,相当于发送请求,开启线程进行RealData的call执行
System.out.println("请求完毕");
try {
Thread.sleep(2000);//这里作为其他业务处理
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("数据="+future.get());//返回数据
}
}
Future接口
boolean cancel(boolean mayInterruptIfRunning);//取消任务
boolean isCancelled();//是否已取消
boolean isDone();//是否已完成
V get() throws InterruptedException, ExecutionException;//取得返回对象
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;//取得返回对象,可以设置超时时间
并行流水线(B+C)*B/2无法并行,B+C没有完成,无法后续
可以将计算拆成三步
P1:A=B+C
P2:D=A*B
P3:D=D/2
public class Msg {
public double i;
public double j;
public String orgStr = null;
// P1算法
public static class Plus implements Runnable {
public static BlockingQueue<Msg> bq = new LinkedBlockingQueue<>();
@Override
public void run() {
while (true) {
try {
Msg msg = bq.take();
msg.j = msg.i + msg.j;
Msg.Multiply.bq.add(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 乘法
public static class Multiply implements Runnable {
public static BlockingQueue<Msg> bq = new LinkedBlockingQueue<>();
@Override
public void run() {
while (true) {
try {
Msg msg = bq.take();
msg.i = msg.i * msg.j;
Div.bq.add(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 除法
public static class Div implements Runnable {
public static BlockingQueue<Msg> bq = new LinkedBlockingQueue<>();
@Override
public void run() {
while (true) {
try {
Msg msg = bq.take();
msg.i = msg.i /2;
System.out.println(msg.orgStr+"="+msg.i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
new Thread(new Plus()).start();
new Thread(new Multiply()).start();
new Thread(new Div()).start();
for(int i=0;i<1000;i++){
for(int j=0;j<1000;j++){
Msg msg = new Msg();
msg.i = i;
msg.j = j;
msg.orgStr ="("+i+"+"+j+")*"+i+"/2";
Plus.bq.add(msg);
}
}
}
}
并行搜索
可以将原始数据集合按照期望的线程数分割,让每个线程独立搜索
public class Array {
static int[] arr;//定义一个数组,需要查找数组中元素
public static class ThreadPool {
static ExecutorService pool = Executors.newCachedThreadPool();//线程池
static final int Thread_Num = 2;//线程数量
static AtomicInteger result = new AtomicInteger(-1);//保存符合条件的元素在arr中的下标,-1表示没有找到
//并发搜索要求每个线程查找数组中的一段位置,指定搜索的起始位置
public static int search(int searchValue, int beginPos, int endPos) {
int i = 0;
for (i = beginPos; i < endPos; i++) {
if (result.get() > 0) {//判断是否有其他线程找到,找到立即返回
return result.get();
}
if (arr[i] == searchValue) {//表示找到了
if (!result.compareAndSet(-1, i)) {//将结果保存result变量中,CAS操作,失败表示其他线程找到,返回结果
return result.get();
}
return i;
}
}
return -1;
}
//查找线程
public static class SearchTask implements Callable<Integer>{
int begin,end,searchValue;
public SearchTask(int begin, int end, int searchValue) {
this.begin = begin;
this.end = end;
this.searchValue = searchValue;
}
@Override
public Integer call() throws Exception {
int re = search(searchValue, begin, end);
return re;
}
}
//并行查找,针对线程数量对arr数组划分,建立对应的任务交给线程池
public static int pSearch(int searchValue) throws InterruptedException, ExecutionException{
int subArrSize=arr.length/Thread_Num+1;
List<Future<Integer>> re = new ArrayList<>();
for(int i=0;i<arr.length;i++){//将线程划分成若干段
int end = i+subArrSize;
if(end>=arr.length)end = arr.length;
re.add(pool.submit(new SearchTask(searchValue, i, end)));
}
//每个任务都会返回future对象,通过result共享彼此信息,当一个线程 成功返回后,其他线程都会返回
for(Future<Integer> fu: re){
if(fu.get()>=0){
return fu.get();
}
}
return -1;
}
}
}
并行排序分离数据相关性:奇偶交换排序
冒泡排序:
public class Test {
public static void main(String[] args) {
int [] arr = {1,10,5,8};
for(int i=0;i<arr.length;i++){
for(int j=0;j<arr.length-1;j++){
if(arr[j]>arr[j+1]){
int temp = arr[j];
arr[j]=arr[j+1];
arr[j+1] = temp;
}
}
}
for(int k=0;k<arr.length;k++){
System.out.println(arr[k]);
}
}
}
每次交换的俩个元素存在数据冲突,每个元素,可能和前面或者后面元素交换,很难并行
奇偶交换排序排序过程分为奇交换和偶交换,奇交换总是比较奇数索引和相邻的后续元素,偶交换总是比较偶索引和其相邻的后续元素,奇交换和偶交换会成对出现,保证比较和交换涉及到每个元素
每一次交换和比较都可以独立进行
public class Test {
public static void main(String[] args) {
int [] arr = {1,10,5,8};
int exchFlag=1,start = 0;//exchFlag记录是否发生交换,start表示哪种交换
while(exchFlag==1||start==1){//上一次比较是奇交换或者发生了数据交换,继续执行
exchFlag=0;
for(int i=start;i<arr.length-1;i+=2){
if(arr[i]>arr[i+1]){
int temp = arr[i+1];
arr[i]=arr[i+1];
arr[i+1]=temp;
exchFlag=0;
}
}
if(start==0){
start=1;
}else{
start=0;
}
}
}
}
并行模式
public class Test {
static int [] arr = {1,10,5,8,3};
static int exchFlag=1;
static ExecutorService pool = Executors.newCachedThreadPool();//线程池
static synchronized void setExchFlag(int v){
exchFlag=v;
}
static synchronized int getExchFlag(){
return exchFlag;
}
public static class OddEvenSortTask implements Runnable{
int i;
CountDownLatch latch;
public OddEvenSortTask(int i, CountDownLatch latch) {//定义了奇偶排序任务类
this.i = i;
this.latch = latch;
}
@Override
public void run() {
if(arr[i]>arr[i+1]){
int temp = arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
setExchFlag(1);
}
latch.countDown();
}
}
public static void pOddEvenSort(int[]arr) throws InterruptedException{//并行排序的主体
int start=0;
while(getExchFlag()==1||start==1){
setExchFlag(0);
//偶数的数组长度,当start为1,只有len/2-1个线程
CountDownLatch latch = new CountDownLatch(arr.length/2-(arr.length%2==0?start:0));//记录线程数量
for(int i=start;i<arr.length-1;i+=2){
pool.submit(new OddEvenSortTask(i, latch));
}
latch.await();//等待所有线程结束
if(start==0){
start=1;
}else{
start=0;
}
}
}
public static void main(String[] args) throws InterruptedException {
pOddEvenSort(arr);
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
}
}
改进的插入排序:希尔排序一个未排序的数组(也可以是链表)可以分为俩部分,前半部分已排序,后半部分未排序,排序时,在未排序的部分中选择一个元素,将其插入有序数组中,直到排完序
public class Test {
public static void main(String[] args) {
int [] arr = {1,10,5,8};
int key,j;
for(int i=1;i<arr.length;i++){
//key为要准备插入的元素
key=arr[i];
j=i-1;
while(j>=0&&arr[j]>key){//确认此元素的插入位置
arr[j+1]=arr[j];
j--;
}
arr[j+1]=key;//插入元素
}
for(int k=0;k<arr.length;k++){
System.out.println(arr[k]);
}
}
}
希尔排序将整个数组根据间隔h分割成若干个子数组,子数组相互穿插在一起,每一次排序,分别对每个子数组排序,每次排序,总是交换间隔为h的俩个元素
以下,所有的方块,圆,三角分别为一个子数组
每一组排序完成后,可以递减h的值,进行下轮更加精细的排序,直到h为1,此时等价于一次插入操作
串行
public class Test {
public static void main(String[] args) {
int [] arr = {5,52,6,3,4,6,2,9,10,7};
//计算出最大的h值
int h=1;
while(h<=arr.length/3){
h=h*3+1;
}
while(h>0){
for(int i=h;i<arr.length;i++){
if(arr[i]<arr[i-h]){
int tmp = arr[i];
int j=i-h;
while(j>=0&&arr[j]>tmp){
arr[j+h]=arr[j];
j-=h;
}
arr[j+h]=tmp;
}
}
//计算出下一个h值
h=(h-1)/3;
}
for(int k=0;k<arr.length;k++){
System.out.println(arr[k]);
}
}
}
并行
public class Test {
static ExecutorService pool = Executors.newCachedThreadPool();// 线程池
static int[] arr = { 5, 52, 6, 3, 4, 6, 2, 9, 10, 7 };
//并行任务,根据给定的起始位置和h进行排序
public static class ShellSortTask implements Runnable {
int i = 0;
int h = 0;
CountDownLatch l;
public ShellSortTask(int i, int h, CountDownLatch l) {
this.i = i;
this.h = h;
this.l = l;
}
@Override
public void run() {
if (arr[i] < arr[i - h]) {
int tmp = arr[i];
int j = i - h;
while (j >= 0 && arr[j] > tmp) {
arr[j + h] = arr[j];
j -= h;
}
arr[j + h] = tmp;
}
l.countDown();
}
}
public static void pShellSort(int[] arg) throws InterruptedException {
// 计算出最大h值
int h = 1;
CountDownLatch latch = null;
while (h < arr.length / 3) {
h = h * 3 + 1;
}
while (h > 0) {
System.out.println("h=" + h);
if (h >= 4) {
latch = new CountDownLatch(arr.length - h);
}
for (int i = h; i < arr.length; i++) {
// 控制线程数量
if(h>=4){
pool.execute(new ShellSortTask(i, h, latch));
}else{
if (arr[i] < arr[i - h]) {
int tmp = arr[i];
int j = i - h;
while (j >= 0 && arr[j] > tmp) {
arr[j + h] = arr[j];
j -= h;
}
arr[j + h] = tmp;
}
}
}
//等待线程排序完成,进入下一次排序
latch.await();
// 计算出下一个h值
h = (h - 1) / 3;
}
}
public static void main(String[] args) throws InterruptedException {
pShellSort(arr);
for (int k = 0; k < arr.length; k++) {
System.out.println(arr[k]);
}
}
}
矩阵乘法
第一个矩阵的列数和第二个矩阵的行数必须相同,如下图,矩阵A*矩阵B,新矩阵中每一个元素为矩阵A和B对应行列的乘积求和
基于Socket的服务端的多线程模式