创建多线程的方法
- new一个Thread时传入一个实现了Runnable接口的类的实例对象
- 继承Thread类,重写Thread类的run方法
线程休眠的方法
Thread.sleep
Interrupts
每一个线程都有一个interrupt status的flag,如果该线程被interrupt的话,这个标志位就会为true。
使用Thread的static方法Thread.interruped()
可以返回该标志位的值,同时清除这个标志位。
使用Thread的non-static方法threadObject.isInterrupted()
可以查看该线程的标志位,但是不清除这个标志位。
threadObject可以使用interrupt
方法来interrupr这个thread。
怎样使线程supporting inerruption?
- If the thread is frequently invoking methods that throw
InterruptedException
, it simply returns from therun
method after it catches that exception. (比如Thread.sleep()
方法就会抛出InterruptedException
) - What if a thread goes a long time without invoking a method that throws
InterruptedException
? Then it must periodically invoke Thread.interrupted
, which returnstrue
if an interrupt has been received.
Joins
join
(非static)方法使得当前线程等待另一线程的完成。
如果t
是一个Thread
实例,那么t.join()
会使得当前线程等待t线程的完成。
线程之间交互可能存在的问题:
Threads communicate primarily by sharing access to fields and the objects reference fields refer to. This form of communication is extremely efficient, but makes two kinds of errors possible: thread interference and memory consistency errors. The tool needed to prevent these errors is synchronization.
However, synchronization can introduce thread contention, which occurs when two or more threads try to access the same resource simultaneously and cause the Java runtime to execute one or more threads more slowly, or even suspend their execution. Starvation and livelock are forms of thread contention.
首先是thread interference:
Interference happens when two operations, running in different threads, but acting on the same data, interleave. This means that the two operations consist of multiple steps, and the sequences of steps overlap. 这种bug很难被发现和修正
其次是memory consistency errors:
Memory consistency errors occur when different threads have inconsistent views of what should be the same data. The causes of memory consistency errors are complex and beyond the scope of this tutorial.
The key to avoiding memory consistency errors is understanding the happens-before relationship.We’ve already seen two actions that create happens-before relationships.
- When a statement invokes Thread.start, every statement that has a happens-before relationship with that statement also has a happens-before relationship with every statement executed by the new thread. The effects of the code that led up to the creation of the new thread are visible to the new thread.
- When a thread terminates and causes a Thread.join in another thread to return, then all the statements executed by the terminated thread have a happens-before relationship with all the statements following the successful join. The effects of the code in the thread are now visible to the thread that performed the join.
Synchronized methods & Synchronized statements
首先是synchronized methods
class Counter {
private int c = 0;
public synchronized void increment() {
c++;
}
public synchronized void decrement() {
c--;
}
public synchronized int value() {
return c;
}
}
给方法加上synchronized关键字由如下两个效果:
- First, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.
- Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads.
虽然synchronized methods可以防止thread interference和memory consistency, 但是可能会带来liveness的问题.
Intrinsic Locks & Synchronization
什么是Intrinsic lock?
intrinsic lock有什么功能?
- enforcing exlusive accesss to an object’s state.
- When a thread releases an intrinsic lock, a happens-before relationship is established between that action and any subsequent acquisition of the same lock.
静态的synchronized method:
You might wonder what happens when a static synchronized method is invoked, since a static method is associated with a class, not an object. In this case, the thread acquires the intrinsic lock for the Class object associated with the class. Thus access to class’s static fields is controlled by a lock that’s distinct from the lock for any instance of the class.
synchronized statements:
public class MsLunch {
private long c1 = 0;
private long c2 = 0;
private Object lock1 = new Object();
private Object lock2 = new Object();
public void inc1() {
synchronized(lock1) {
c1++;
}
}
public void inc2() {
synchronized(lock2) {
c2++;
}
}
}
Reentrant lock:
防止线程自己阻塞自己
Atomic Access
atomic 操作可以防止thread interference,但是不可以防止memory consistence errors.
常见的atomic action:
- Reads and writes are atomic for reference variables and for most primitive variables (all types except
long
anddouble
). - Reads and writes are atomic for all variables declared
volatile
(includinglong
anddouble
variables).
Atomic actions cannot be interleaved, so they can be used without fear of thread interference. However, this does not eliminate all need to synchronize atomic actions, because memory consistency errors are still possible. Using
volatile
variables reduces the risk of memory consistency errors, because any write to avolatile
variable establishes a happens-before relationship with subsequent reads of that same variable. This means that changes to avolatile
variable are always visible to other threads. What’s more, it also means that when a thread reads avolatile
variable, it sees not just the latest change to thevolatile
, but also the side effects of the code that led up the change.
java.util.concurrency
中的一些类提供了原子操作
Liveness
Deadlock
public class Deadlock {
static class Friend {
private final String name;
public Friend(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public synchronized void bow (Friend bower) {
System.out.printf("%s: %s" + " has bowed to me!%n", this.name, bower.getName());
bower.bowBack(this);
}
public synchronized void bowBack(Friend bower) {
System.out.printf("%s: %s " + "has bowed back to me%n", this.name, bower.getName());
}
}
public static void main(String[] args) {
final Friend alphonse =
new Friend("Alphonse");
final Friend gaston =
new Friend("Gaston");
new Thread(new Runnable() {
public void run() { alphonse.bow(gaston); }
}).start();
new Thread(new Runnable() {
public void run() { gaston.bow(alphonse); }
}).start();
}
}
什么是Starvation,什么是Livelock
走廊里相互礼让的例子解释deadlock和livelock
Guarded block
线程检验某条件成立时才继续往下执行
使用wait, notify的方法
线程调用某实例的wait方法时必须拥有此实例的intrinsic lock
Lock Objects
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
Executors
在java.util.concurrent
中定义的三种executor接口:
Executor
ExecutorService
是Exexutor
的子接口ScheduledExecutorService
是ExecutorService
的子接口
Typically, variables that refer to executor objects are declared as one of these three interface types, not with an executor class type.
Executor
接口
可以直接使用Executor
接口中提供的exrcute
方法来代替原始的创建线程的方法.如果r
是一个Runnable
object, e
是一个Executor
object的话,呢么下面两个语句相等:
(new Thread(r)).start();
和 e.execute(r);
有不同的是,第一种方式直接new一个新的线程并且立即启动这个新的线程, 第二种方式很有可能利用一个已经存在的worker thread去runr
, 或者将r
放入一个队列中,等待一个worker thread空闲的时候再runr
.
ExecutorService
接口
ScheduledExecutorService
接口
Thread Pools
java.util.concurrent
大多数对的executor实现都使用了***thread pools***, thread pools由***worker thread***组成.worker thread和它执行的Runnable
或者Callable
任务是分离的, 经常被用于执行多任务.
thread pool的一种常见类型是***fixed thread pool***
fork/join
是一种框架, 对ExecutorService
的实现,为了尽可能地利用系统地多核处理能力,应用场景为可以层层细分地任务.
work-steal 算法
核心是java.util.ForkJoinPool
类, 父类是AbstractExecutorService
类.
为了使用fork/join框架,首先要实现下面一段代码:
if (my portion of the work is small enough)
do the work directly
else
split my work into two pieces
invoke the two pieces and wait for the results
然后:Wrap this code in a ForkJoinTask
subclass, typically using one of its more specialized types, either RecursiveTask
(which can return a result) or RecursiveAction
.
最后After your ForkJoinTask
subclass is ready, create the object that represents all the work to be done and pass it to the invoke()
method of a ForkJoinPool
instance.
atomic variables
可以同时解决交织和内存不连续地问题
java.util.cuncurrent.atomic
concurrent random numbers
java.util.concurrent.THreadLocalRandom
int r = ThreadLocalRandom.current().nextInt(4, 77);