小结:

1、基于java并发模型

Scala concurrency is built on top of the Java concurrency model.

2、

将每个请求放入一个新的线程

The main drawback with this code is that only one request at a time can be answered!

You could put each request in a Thread. Simply change

(new Handler(socket)).run()

to

(new Thread(new Handler(socket))).start()

3、future代表异步计算,实例承载尚未得出的结果

Future represents an asynchronous computation. You can wrap your computation in a Future and when you need the result, you simply call a blocking Await.result() method on it. An Executor returns a Future. If you use the Finagle RPC system, you use Future instances to hold results that might not have arrived yet.

4、2个线程的实例引用同一个的对象的实例,对象属性值被线程不安全地修改;

class Person(var name: String) {
def set(changedName: String) {
name = changedName
}
}

This program is not safe in a multi-threaded environment. If two threads have references to the same instance of Person and call set, you can’t predict what name will be at the end of both calls.

http://twitter.github.io/scala_school/concurrency.html

Runnable/Callable

Runnable has a single method that returns no value.

trait Runnable {
def run(): Unit
}

Callable is similar to run except that it returns a value

trait Callable[V] {
def call(): V
}

Threads

Scala concurrency is built on top of the Java concurrency model.

On Sun JVMs, with a IO-heavy workload, we can run tens of thousands of threads on a single machine.

A Thread takes a Runnable. You have to call start on a Thread in order for it to run the Runnable.

scala> val hello = new Thread(new Runnable {
def run() {
println("hello world")
}
})
hello: java.lang.Thread = Thread[Thread-3,5,main] scala> hello.start
hello world

When you see a class implementing Runnable, you know it’s intended to run in a Thread somewhere by somebody.

Something single-threaded

Here’s a code snippet that works but has problems.

import java.net.{Socket, ServerSocket}
import java.util.concurrent.{Executors, ExecutorService}
import java.util.Date class NetworkService(port: Int, poolSize: Int) extends Runnable {
val serverSocket = new ServerSocket(port) def run() {
while (true) {
// This will block until a connection comes in.
val socket = serverSocket.accept()
(new Handler(socket)).run()
}
}
} class Handler(socket: Socket) extends Runnable {
def message = (Thread.currentThread.getName() + "\n").getBytes def run() {
socket.getOutputStream.write(message)
socket.getOutputStream.close()
}
} (new NetworkService(2020, 2)).run

Each request will respond with the name of the current Thread, which is always main.

The main drawback with this code is that only one request at a time can be answered!

You could put each request in a Thread. Simply change

(new Handler(socket)).run()

to

(new Thread(new Handler(socket))).start()

but what if you want to reuse threads or have other policies about thread behavior?

Executors

With the release of Java 5, it was decided that a more abstract interface to Threads was required.

You can get an ExecutorService using static methods on the Executors object. Those methods provide you to configure an ExecutorService with a variety of policies such as thread pooling.

Here’s our old blocking network server written to allow concurrent requests.

import java.net.{Socket, ServerSocket}
import java.util.concurrent.{Executors, ExecutorService}
import java.util.Date class NetworkService(port: Int, poolSize: Int) extends Runnable {
val serverSocket = new ServerSocket(port)
val pool: ExecutorService = Executors.newFixedThreadPool(poolSize) def run() {
try {
while (true) {
// This will block until a connection comes in.
val socket = serverSocket.accept()
pool.execute(new Handler(socket))
}
} finally {
pool.shutdown()
}
}
} class Handler(socket: Socket) extends Runnable {
def message = (Thread.currentThread.getName() + "\n").getBytes def run() {
socket.getOutputStream.write(message)
socket.getOutputStream.close()
}
} (new NetworkService(2020, 2)).run

Here’s a transcript connecting to it showing how the internal threads are re-used.

$ nc localhost 2020
pool-1-thread-1 $ nc localhost 2020
pool-1-thread-2 $ nc localhost 2020
pool-1-thread-1 $ nc localhost 2020
pool-1-thread-2

Futures

Future represents an asynchronous computation. You can wrap your computation in a Future and when you need the result, you simply call a blocking Await.result() method on it. An Executor returns a Future. If you use the Finagle RPC system, you use Future instances to hold results that might not have arrived yet.

FutureTask is a Runnable and is designed to be run by an Executor

val future = new FutureTask[String](new Callable[String]() {
def call(): String = {
searcher.search(target);
}})
executor.execute(future)

Now I need the results so let’s block until its done.

val blockingResult = Await.result(future)

See Also Scala School’s Finagle page has plenty of examples of using Futures, including some nice ways to combine them. Effective Scala has opinions about Futures .

Thread Safety Problem

class Person(var name: String) {
def set(changedName: String) {
name = changedName
}
}

This program is not safe in a multi-threaded environment. If two threads have references to the same instance of Person and call set, you can’t predict what name will be at the end of both calls.

In the Java memory model, each processor is allowed to cache values in its L1 or L2 cache so two threads running on different processors can each have their own view of data.

Let’s talk about some tools that force threads to keep a consistent view of data.

Three tools

synchronization

Mutexes provide ownership semantics. When you enter a mutex, you own it. The most common way of using a mutex in the JVM is by synchronizing on something. In this case, we’ll synchronize on our Person.

In the JVM, you can synchronize on any instance that’s not null.

class Person(var name: String) {
def set(changedName: String) {
this.synchronized {
name = changedName
}
}
}

volatile

With Java 5’s change to the memory model, volatile and synchronized are basically identical except with volatile, nulls are allowed.

synchronized allows for more fine-grained locking. volatile synchronizes on every access.

class Person(@volatile var name: String) {
def set(changedName: String) {
name = changedName
}
}

AtomicReference

Also in Java 5, a whole raft of low-level concurrency primitives were added. One of them is an AtomicReference class

import java.util.concurrent.atomic.AtomicReference

class Person(val name: AtomicReference[String]) {
def set(changedName: String) {
name.set(changedName)
}
}

Does this cost anything?

@AtomicReference is the most costly of these two choices since you have to go through method dispatch to access values.

volatile and synchronized are built on top of Java’s built-in monitors. Monitors cost very little if there’s no contention. Since synchronized allows you more fine-grained control over when you synchronize, there will be less contention so synchronized tends to be the cheapest option.

When you enter synchronized points, access volatile references, or deference AtomicReferences, Java forces the processor to flush their cache lines and provide a consistent view of data.

PLEASE CORRECT ME IF I’M WRONG HERE. This is a complicated subject, I’m sure there will be a lengthy classroom discussion at this point.

Other neat tools from Java 5

As I mentioned with AtomicReference, Java 5 brought many great tools along with it.

CountDownLatch

CountDownLatch is a simple mechanism for multiple threads to communicate with each other.

val doneSignal = new CountDownLatch(2)
doAsyncWork(1)
doAsyncWork(2) doneSignal.await()
println("both workers finished!")

Among other things, it’s great for unit tests. Let’s say you’re doing some async work and want to ensure that functions are completing. Simply have your functions countDown the latch and await in the test.

AtomicInteger/Long

Since incrementing Ints and Longs is such a common task, AtomicInteger and AtomicLong were added.

AtomicBoolean

I probably don’t have to explain what this would be for.

ReadWriteLocks

ReadWriteLock lets you take reader and writer locks. reader locks only block when a writer lock is taken.

Let’s build an unsafe search engine

Here’s a simple inverted index that isn’t thread-safe. Our inverted index maps parts of a name to a given User.

This is written in a naive way assuming only single-threaded access.

Note the alternative default constructor this() that uses a mutable.HashMap

import scala.collection.mutable

case class User(name: String, id: Int)

class InvertedIndex(val userMap: mutable.Map[String, User]) {

  def this() = this(new mutable.HashMap[String, User])

  def tokenizeName(name: String): Seq[String] = {
name.split(" ").map(_.toLowerCase)
} def add(term: String, user: User) {
userMap += term -> user
} def add(user: User) {
tokenizeName(user.name).foreach { term =>
add(term, user)
}
}
}

I’ve left out how to get users out of our index for now. We’ll get to that later.

Let’s make it safe

In our inverted index example above, userMap is not guaranteed to be safe. Multiple clients could try to add items at the same time and have the same kinds of visibility errors we saw in our first Person example.

Since userMap isn’t thread-safe, how do we keep only a single thread at a time mutating it?

You might consider locking on userMap while adding.

def add(user: User) {
userMap.synchronized {
tokenizeName(user.name).foreach { term =>
add(term, user)
}
}
}

Unfortunately, this is too coarse. Always try to do as much expensive work outside of the mutex as possible. Remember what I said about locking being cheap if there is no contention. If you do less work inside of a block, there will be less contention.

def add(user: User) {
// tokenizeName was measured to be the most expensive operation.
val tokens = tokenizeName(user.name) tokens.foreach { term =>
userMap.synchronized {
add(term, user)
}
}
}

SynchronizedMap

We can mixin synchronization with a mutable HashMap using the SynchronizedMap trait.

We can extend our existing InvertedIndex to give users an easy way to build the synchronized index.

import scala.collection.mutable.SynchronizedMap

class SynchronizedInvertedIndex(userMap: mutable.Map[String, User]) extends InvertedIndex(userMap) {
def this() = this(new mutable.HashMap[String, User] with SynchronizedMap[String, User])
}

If you look at the implementation, you realize that it’s simply synchronizing on every method so while it’s safe, it might not have the performance you’re hoping for.

Java ConcurrentHashMap

Java comes with a nice thread-safe ConcurrentHashMap. Thankfully, we can use JavaConverters to give us nice Scala semantics.

In fact, we can seamlessly layer our new, thread-safe InvertedIndex as an extension of the old unsafe one.

import java.util.concurrent.ConcurrentHashMap
import scala.collection.JavaConverters._ class ConcurrentInvertedIndex(userMap: collection.mutable.ConcurrentMap[String, User])
extends InvertedIndex(userMap) { def this() = this(new ConcurrentHashMap[String, User] asScala)
}

Let’s load our InvertedIndex

The naive way

trait UserMaker {
def makeUser(line: String) = line.split(",") match {
case Array(name, userid) => User(name, userid.trim().toInt)
}
} class FileRecordProducer(path: String) extends UserMaker {
def run() {
Source.fromFile(path, "utf-8").getLines.foreach { line =>
index.add(makeUser(line))
}
}
}

For every line in our file, we call makeUser and then add it to our InvertedIndex. If we use a concurrent InvertedIndex, we can call add in parallel and since makeUser has no side-effects, it’s already thread-safe.

We can’t read a file in parallel but we can build the User and add it to the index in parallel.

A solution: Producer/Consumer

A common pattern for async computation is to separate producers from consumers and have them only communicate via a Queue. Let’s walk through how that would work for our search engine indexer.

import java.util.concurrent.{BlockingQueue, LinkedBlockingQueue}

// Concrete producer
class Producer[T](path: String, queue: BlockingQueue[T]) extends Runnable {
def run() {
Source.fromFile(path, "utf-8").getLines.foreach { line =>
queue.put(line)
}
}
} // Abstract consumer
abstract class Consumer[T](queue: BlockingQueue[T]) extends Runnable {
def run() {
while (true) {
val item = queue.take()
consume(item)
}
} def consume(x: T)
} val queue = new LinkedBlockingQueue[String]() // One thread for the producer
val producer = new Producer[String]("users.txt", q)
new Thread(producer).start() trait UserMaker {
def makeUser(line: String) = line.split(",") match {
case Array(name, userid) => User(name, userid.trim().toInt)
}
} class IndexerConsumer(index: InvertedIndex, queue: BlockingQueue[String]) extends Consumer[String](queue) with UserMaker {
def consume(t: String) = index.add(makeUser(t))
} // Let's pretend we have 8 cores on this machine.
val cores = 8
val pool = Executors.newFixedThreadPool(cores) // Submit one consumer per core.
for (i <- i to cores) {
pool.submit(new IndexerConsumer[String](index, q))
}

Built at @twitter by @stevej@marius, and @lahosken with much help from @evanm@sprsquish@kevino@zuercher@timtrueman@wickman,@mccv and @garciparedes; Russian translation by appigram; Chinese simple translation by jasonqu; Korean translation by enshahar;

Licensed under the Apache License v2.0.

 
 
 

线程安全 Thread Safety Problem scala concurrency 并发的更多相关文章

  1. 折返(Reentrancy)VS线程安全(Thread safety)

    在Wiki上,折返例如,下面的定义(接) In computing, a computer program or subroutine is called reentrant if it can be ...

  2. Thread Safety线程安全

    Thread Safe(线程安全)和None Thread Safe(NTS,非线程安全)之分 如果disabled就选择nts(php_stomp-1.0.9-5.5-nts-vc11-x86.zi ...

  3. Java Concurrency In Practice -Chapter 2 Thread Safety

    Writing thread-safe code is managing access to state and in particular to shared, mutable state. Obj ...

  4. 14.并发与异步 - 1.线程处理Thread -《果壳中的c#》

    14.2.1 创建一个线程 实例化一个Thread对象,然后调用它的Start方法,就可以创建和启动一个新的线程.最简单的Thread构造方法是接受一个ThreadStart代理:一个无参方法,表示执 ...

  5. clang的线程安全分析模块 thread safety analysis

    介绍 Clang的线程安全分析模块是C++语言的一个扩展,能对代码中潜在的竞争条件进行警告.这种分析是完全静态的(即编译时进行),没有运行时的消耗.当前这个功能还在开发中,但它已经具备了足够的成熟度, ...

  6. java并发编程学习: 守护线程(Daemon Thread)

    在正式理解这个概念前,先把 守护线程 与 守护进程 这二个极其相似的说法区分开,守护进程通常是为了防止某些应用因各种意外原因退出,而在后台独立运行的系统服务或应用程序. 比如:我们开发了一个邮件发送程 ...

  7. JAVA并发编程——守护线程(Daemon Thread)

    在Java中有两类线程:用户线程 (User Thread).守护线程 (Daemon Thread). 所谓守护 线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称 ...

  8. Thread Safety in Java(java中的线程安全)

    Thread Safety in Java is a very important topic. Java provide multi-threaded environment support usi ...

  9. python3 线程 threading.Thread GIL性能详解(2.3)

    python3 线程 threading 最基础的线程的使用 import threading, time value = 0 lock = threading.Lock() def change(n ...

随机推荐

  1. R语言︱处理缺失数据&&异常值检验、离群点分析、异常值处理

    在数据挖掘的过程中,数据预处理占到了整个过程的60% 脏数据:指一般不符合要求,以及不能直接进行相应分析的数据 脏数据包括:缺失值.异常值.不一致的值.重复数据及含有特殊符号(如#.¥.*)的数据 数 ...

  2. Unity应用架构设计(8)——使用ServiceLocator实现对象的注入

    对象的 『注入』 是企业级软件开发经常听到的术语.如果你是一个 Java 程序员,一定对注入有着深刻的映像.不管是SSH框架还是SSM框架,Spring 全家桶永远是绕不过去的弯.通过依赖注入,可以有 ...

  3. web字体分析

    一.衬线字体与非衬线字体 衬线体(serif)和无衬线体(sans-serif)的分类起源于英文字体界. 衬线体(serif)-Georgia-Times 「衬线」指的是字形笔画在首位的装饰和笔画的粗 ...

  4. PHP事件机制

    先用 3W1H(who what why how) 分析法的思路来解释一下 事件机制, 更重要的是, 这个有什么用. 正常的程序执行, 或者说人的思维趋势, 都是按照 时间线性串行 的, 保持 连续性 ...

  5. 转-编写CGI小结

    由于Carl要用到我的程序,我们便合作工作.但是他写的程序是Python的,我写的程序是Java的,必须得找一种方式进行通信.尽管有Jython这些东西,但是Carl认为还是CGI最简便.于是,前阵子 ...

  6. 原创科幻短篇《VR》

    近些年VR很火,现在似乎又降温了,那么问题来了:VR到底有前景吗?我农村来的读书又少看不清楚哇.近些年房地产很火,现在似乎还是很火,那么问题来了:房价到底会降吗?我农村来的读书又少看不清楚哇. 以下正 ...

  7. jQuery雷达扫描切换幻灯片代码

    基于jQuery雷达扫描切换幻灯片代码.这是一款切换效果类似雷达扫描,支持鼠标滚轮滚动切换.效果图如下: 在线预览   源码下载 实现的代码. html代码: <div class=" ...

  8. 【C#】读取Excel中嵌套的Json对象,Json带斜杠的问题(其三)

    除了上一篇中提到的对字符串的字符替换操作,去掉Json中的转义符反斜杠\之外,还发现了更加简单的办法. 就是使用Newtownsoft.Json序列化Json时,将嵌套的Json对象(字符串)转为JO ...

  9. MOD(motion Object Detection)介绍

    Motion Detection or Moving Object Detection 称之为运动侦测,移动侦测,移动检测 MOD全称为Moving Object Detection,中文“移动物体检 ...

  10. [tomcat启动报错]registered the JDBC driver [com.alibaba.druid.proxy.DruidDriver] but failed to unregister it when the web application was stopped

    环境:一个tomcat ,一个工程配置了多数据源,在启动的时候报如下错误: SEVERE: The web application [/qdp-resource-job] registered the ...