第五章-并行模式与算法

5.1 探讨单例模式 209

单例模式非常常见,我们不可避免会在多线程环境中使用它们。并且,系统中使用单例的地方可能非常频繁,因此我们非常迫切地需要一种高效的单例实现。

第一种实现方式(饿汉式)
public class Singleton 
private Singleton(){
System.out.println("Singleton is create");
private static Singleton instance = newSingleton();
public static Singleton getInstance() {
       return instance;
  }
}

缺点:Singleton实例在什么时候创建是不受控制的。即使系统没有要求创建单例,newSingleton()方法也会被调用。

第二种实现方式(延迟加载策略/懒汉式)
public class LazySingleton {
private LazySingleton() {
     System.out.println("LazySingleton is create");
}
private static LazySingleton instance = null;
public static synchronized LazySingleton getInstance() {
            if(instance == null)
          instance = new LazySingleton();
          return instance;
          }
}

只在真正需要时创建对象。但坏处也很明显,并发环境下加锁,竞争激烈的场合对性能可能产生一定的影响。但总体上,这是一个非常易于实现和理解的方法。

第三种实现方式:双重检查模式(作者不推荐)
第四种实现方式(结合了前两种方式的优点)
public class StaticSingleton (
  private StaticSingleton() {
      System. out. println ("StaticSingleton is create") ;
}
  private static class SingletonHolder {
  private static StaticSingleton instance = new StaticSingleton();
  }
  public static StaticSingleton getInstance() {
      return SingletonHolder.instance;
  }
}

5.2 不变模式 213

核心:一个对象一旦被创建,则它的内部状态将永远不会发生改变。

不变模式的实现:

  • 去除所有的setter方法以及所有修改自身属性的方法。
  • 属性设为私有,并且final标记,确保其不可修改。
  • 确保子类可以重载修改它的行为。
  • 有一个可以创建完整对象的构造函数。

使用不变模式的例子:元数据的包装类和String类

5.3 生产者-消费者模式 215

用BlockigQueue队列作为共享内存缓存区不是一个高性能的实现,它完全使用锁和阻塞等待来实现线程间的同步。转而使用ConcurrentLinkedQueue ,其是一个高性能的队列,而ConcurrentLinkedQueue队列使用了大量无锁的CAS操作。我们可以使用CAS来实现生产者-消费者模式从而获得性能提升。但使用CAS进行编程是非常困难的,目前有一个现成的Disruptor框架,它已经帮助我们实现了这一个功能。

5.4 高性能的生产者-消费者模式:无锁的实现 220

5.4.1 无锁的缓存框架:Disruptor 221

5.4.2 用Disruptor框架实现生产者-消费者模式的案例 222

5.4.3 提高消费者的响应时间:选择合适的策略 225

5.4.4 CPU Cache的优化:解决伪共享问题 226

5.5 Future模式 230

Future模式的核心思想是异步调用,可以让被调用者先立即返回一个”契约“(虚拟数据),然后让其在后台慢慢处理这个请求。调用者可以先处理其他任务。等结果准备好了,再返回给调用者。

5.5.1 Future模式的主要角色 232

5.5.2 Future模式的简单实现 233

5.5.3 JDK中的Future模式 236

下面展示JDK内置的Future模式的使用方法

public class RealData implements Callable<String> {
    private String para;
    public RealData (String para) {
        this para=para;
    }
    @Override
    public String call() throws Exception { //返回的结果比较慢
      
        StringBuffer sb=new StringBuffer() ;
        for (int i= 0;i< 10; i++) [
            sb.append (para) ;
            try {
                Thread.sleep (100) ;
            } catch (InterruptedException e) {
            }
          }
        return sb. toString();
     }
}
public class FutureMain {
    public static void main (String[] args) throws InterruptedException, ExecutionException {
//构造FutureTask
        FutureTask<String> future = new FutureTask<String> (new RealData("a"));
      //future是返回的契约
        ExecutorService executor = Executors.newFixedThreadPool (1) ;
        //执行FutureTask,相当于上例中的client. request ("a")发送请求
        //在这里开启线程进行RealData的call ()方法执行
        executor.submit (future);
      
        System. out.println ("请求完毕");
      try {
        //这里依然可以做额外的数据操作,使用sleep代替其他业务逻辑的处理
        Thread. sleep (2000) ;  //等待的时候,做一些其他的事情
        } catch (InterruptedException e) {
      }
        //相当于5.5.2节中的data. getResult ()方法,取得call ()方法的返回值
        //如果此时call ()方法没有执行完成,则依然会等待
        System.out.println("数据= " + future.get());  //最后从契约中拿数据
    }
}

5.5.4 Guava对Future模式的支持 238

5.6 并行流水线 240

执行过程中,有数据相关性的运算都是无法完美并行化的,可以将有依赖的操作分配在不同的线程进行计算,从而提升效率。(实际上就是生活中的流水线思想)

5.7 并行搜索 244

并行搜索的策略是将原始数据集合按照期望的线程数进行分割。如果我们计划使用两个线程进行搜索,那么就可以把一个数组或集合分割成两个。每个线程各自独立搜索,当其中有一个线程找到数据后,立即返回结果即可。

5.8 并行排序 246

5.8.1 分离数据相关性:奇偶交换排序 246

奇偶交换排序:分离了数据的相关性

算法步骤

  1. 选取所有奇数列的元素与其右侧相邻的元素进行比较,将较小的元素排序在前面;
  2. 选取所有偶数列的元素与其右侧相邻的元素进行比较,将较小的元素排序在前面;
  3. 重复前面两步,直到所有序列有序为止。

https://blog.csdn.net/lemon_tree12138/article/details/50605563

5.8.2 改进的插入排序:希尔排序 250

希尔排序将整个数组根据间隔h分割为若千个子数组,它们相互穿插在一起,每一次排序时,分别对每一个子数组进行排序。

5.9 并行算法:矩阵乘法 254

如果需要进行并行计算,一种简单的策略是将矩阵A进行水平分割,得到子矩阵A1和A2,矩阵B进行垂直分割,得到子矩阵B1和B2。此时,我们只要分别计算这些子矩阵的乘积,再将结果进行拼接就能得到原始矩阵A和B的乘积。

当然,这个过程是可以反复进行的。为了计算矩阵A1XB1,我们还可以进一步将矩阵A1和矩阵B1分解,直到我们认为子矩阵的大小已经在可接受范围内。

我们使用Fork/Join 框架来实现并行矩阵相乘的想法,使用jMatrices开源软件,作为矩阵计算的工具。

5.10 准备好了再通知我:网络NIO 258

Java NIO是NewIO的简称,它是–种可以替代Java IO的一套新的IO机制。它提供了一套不同于Java标准IO的操作机制。严格来说,NIO与并发并无直接的关系,但是使用NIO技术可以大大提高线程的使用效率。

5.10.1 基于Socket的服务端多线程模式 259

服务器会为每一个客户端连接启用一个线程,这个新的线程将全心全意为这个客户端服务。同时,为了接受客户端连接,服务器还会额外使用一个派发线程。

这种模式的缺点是:倾向于让CPU进行IO等待

例如:如果服务端缓慢地向客户端输入,则服务端的请求处理时间就会变长。因为服务器要先读入客户端的输入,而客户端缓慢的处理速度(当然也可能是一个拥挤的网络环境)使得服务器花费了不少等待时间。

我们可以试想一下,服务器要处理大量的请求连接,如果每个请求都像这样拖慢了服务器的处理速度,那么服务端能够处理的并发数量就会大幅度减少。

在这个案例中,服务器处理请求之所以慢,并不是因为在服务端有多少繁重的任务,而是因为服务线程在等待IO而已。让高速运转的CPU去等待极其低效的网络IO是非常不合算的行为。

5.10.2 使用NIO进行网络编程 264

己注册到一个Selector 中,因此这个Channel就能为Selector所管理。而一一个Selector可以
管理多个SelectableChannel。当SelectableChannel的数据准备好时,Selector 就会接到通知,
得到那些已经准备好的数据,而SocketChannel就是SelectableChannel的- -种。因此,它们
构成了如图5.20所示的结构。

使用Java的NIO就可以将上节的网络IO等待时间从业务处理线程中抽取出来。

NIO中的又一个关键组件Channel。Channel有点类似于流,一个Channel可以和文件或者网络Socket对应。如果Channel对应着一个Soceket,那么往这个Channel中写数据,就等于向Socket中写入数据。

数据需要包装成Buffer的形式才能和Channel交互。 Buffer理解成一个内存区域或者byte数组。

Selector是选择器。SelectableChannel是Channel的一个实现,表示可被选择的通道。任何一个SelectableChannel都可以将自己注册到一个Selector中,即由Selector 所管理。

一个Selector可以由一个线程进行管理,一个Selector可以管理多个SelectableChannel。当SelectableChannel的数据准备好时,Selector 就会接到通知,得到那些已经准备好的数据

结构如图:

5.10.3 使用NIO来实现客户端 272

5.11 读完了再通知我:AIO 274

AIO是异步IO的缩写,即Asynchronized IO。

NIO是同步的,但对于AIO来说,则更进了一步,它不是在IO准备好时再通知线程,而是在IO操作
已经完成后,再给线程发出通知。因此,AIO是完全不会阻塞的。此时,我们的业务逻辑将变成一个回调函数,等待IO操作完成后,由系统自动触发。

5.11.1 AIO EchoServer的实现 275

5.11.2 AIO Echo客户端的实现 277

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Scikit-learn是一个基于Python的机器学习库,它有以下优点: 1. 易于使用: scikit-learn提供了一个统一的界面,可以简化机器学习任务的实现。 2. 高效: scikit-learn内部使用了大量优化过的算法, 它可以高效地处理大规模数据. 3. 可扩展性: scikit-learn提供了大量的可扩展性选项,如并行计算, 可以满足大规模学习的需求. 4. 丰富的文档和社区支持: scikit-learn有丰富的文档和教程,并有一个活跃的社区可以提供帮助. 5. 可以方便的和其他科学计算库配合使用: scikit-learn可以与 NumPy, pandas 和 Matplotlib 等科学计算库很好地配合使用. ### 回答2: Scikit-learn是一个强大的Python机器学习库,具有以下几个优点。 首先,Scikit-learn具有简单易用的接口和一致的编程模式,使得用户能够轻松地构建和实现机器学习算法。它提供了丰富的工具和函数,可以用于数据预处理、特征选择、模型评估等多个环节,大大简化了机器学习流程。 其次,Scikit-learn拥有丰富的机器学习算法和模型库。它覆盖了包括分类、回归、聚类、降维等多个领域的算法,包括支持向量机(SVM)、随机森林、K均值聚类等等。这使得用户可以根据自己的需求选择适合的算法,同时也为教育和研究人员提供了便利。 第三,Scikit-learn具有良好的性能和可扩展性。它底层采用了NumPy、SciPy和Cython等高性能的科学计算库,可以处理大规模数据集和高维特征。此外,Scikit-learn还支持并行计算和分布式计算,利用多核CPU和集群计算资源,进一步提升算法的运行效率。 最后,Scikit-learn是一个开源项目,拥有庞大的社区支持和活跃的开发者社区。这意味着用户可以很容易地获取相关的文档、示例代码和技术支持。同时,开源的特点也使得Scikit-learn能够持续获得改进和更新,保持与最新的研究成果和技术进展保持同步。 综上所述,Scikit-learn作为一个强大的Python机器学习库,具有简单易用的接口、丰富的算法库、良好的性能和可扩展性以及庞大的社区支持。无论是对于机器学习初学者还是专业从业者,Scikit-learn都是一个值得推荐的工具。 ### 回答3: Scikit-learn是一个流行的Python机器学习库,具有以下几个优点: 1. 易于使用:Scikit-learn为用户提供了简单且一致的API,使得数据预处理、特征工程、模型训练和评估的过程变得简单易懂。对于初学者和有经验的用户来说,Scikit-learn是一个非常友好的工具。 2. 全面的功能:Scikit-learn提供了丰富的机器学习算法和工具,包括分类、回归、聚类、降维、模型选择等等。这使得用户能够方便地使用不同的模型来解决各种问题,满足各种应用的需求。 3. 高性能:Scikit-learn是用Cython实现的,在速度和性能方面表现出色。它能够处理大规模的数据集,并且对内存使用进行了优化。此外,Scikit-learn还支持并行化处理,可以在多核处理器上进行高效计算。 4. 强大的文档和社区支持:Scikit-learn拥有完善的文档,包括详细的教程、示例和API文档。用户可以轻松地查找和学习需要的知识。此外,Scikit-learn拥有活跃的社区,用户可以在论坛上提问、分享和交流,得到及时的帮助和反馈。 5. 兼容性和扩展性:Scikit-learn与其他Python库和工具具有良好的兼容性,例如NumPy、SciPy和Pandas等。它也可以与其他机器学习库和框架集成,如TensorFlow和PyTorch。这使得用户可以方便地在不同的环境中使用Scikit-learn,并根据需要进行扩展和定制。 总之,Scikit-learn作为一个全面而强大的机器学习库,拥有易用性、性能优秀、文档丰富和社区活跃等优点,使得它成为数据科学家和机器学习从业者的首选工具。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值