分类 Java 相关 下的文章

Java 线程同步 Synchronized 和 Lock

synchronized关键字

synchronized关键字可以作用于代码块,也可以作用于方法声明. 若作用于代码块, 必须显式指明使用那个对象或类的monitor锁. 若作用于方法: 方法是static, 则使用类的monitor, 否则使用对象实例的monitor.
若已获得某对象或类的monitor, 可再次进入该对象或类monitor 作用的块或方法.
被synchronized的语句要尽量少, 以提高性能;
若某线程已经通过synchronized获得A的monitor, 又获得B的monitor,

synchronized与 wait(), notify(), notifyAll()

wait(), notify(), notifyAll()必须在获得同一对象实例或类的的monitor的 synchronized的语句中, 否则JVM会抛出IllegalMonitorStateException.
当wait() 方法被调用之后, 当前线程释放该对象或实例的monitor, 并且挂起, 其它等待该monitor的线程获得该monitor, 进入被保护的synchronized语句运行, 直到有获得同样monitor的线程调用notify() 或notifyAll() 方法, 并且释放该monitor, 挂起的线程才有可能恢复运行.
另外 wait(), notify(), notifyAll() 方法都会抛出 InterruptedException, 要注意处理;wait() 一般处在一个while循环中, 每次醒来都去check一下当前的条件是不是已经被满足.

Intrinsic Locks and Synchronization 内置锁和synchronized

Java的synchronized是建立在所有对象内置的一个内置锁. 在一般的教程或API中一般称之为 intrinsic lock or monitor lock, 有时候也简单的称之为 monitor.
这里有更详细的说明: https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html

多个synchronized 的锁获得与释放顺序

在JDK Lock的文档看到这么一句讲synchronized锁的: when multiple locks are acquired they must be released in the opposite order. 其实锁并不需要这个顺序, 只不过这是synchronized的语句结构造成的, 每个synchronized的语句都有一对大括号用来显示指明起锁的代码范围, 词法结构造成了这个获得与release的顺序. 所以也就有了接下来的这句: all locks must be released in the same lexical scope in which they were acquired.
这也是synchronized与Lock的重要区别. 当然这种结构也使这种锁更简单, 不容易出错.

Lock 接口 的 non-blocking 特性

Lock 相对于synchronized的最大优点是, 可以尝试去获得Lock, 而不是一直等待:
boolean tryLock() //试图获得, 当lock free 的时候便可以获得;
boolean tryLock(long time, TimeUnit unit) // 在给定时间内尝试获得, 并且线程没被interrupted.
void lockInterruptibly() // 尝试获得, 除非当前线程被interrupted
同时Lock 也可以通过与之关联的Condition 类实现了 wait/notify 机制;

Lock 相对于synchronized的其它特性

获得锁的顺序: 公平锁, 不公平锁;
non-reentrant : synchronized一定是reentrant的, Lock 可以做到不允许reentrant (参考具体实现);
死锁检测:

Lock Condition

Lock取代了synchronized关键字, Condition取代了synchronized需要的内置锁.
Object 的wait()/notify()/notifyAll() 方法依赖对象或类的内置锁实现等待和唤醒, Lock的子类则通过Condition实现线程的等待和唤醒, Condition有对应的await(), signal() 方法.
Object的wait()/notify()/notifyAll() 方法只能依赖该对象的唯一内置锁, 而Lock可以依赖多个Condition, 同一把锁, 有多个开始等待和唤醒的条件, 那么就可以实现根据不同的条件, 唤醒正在等待这把锁的多个线程中特定的线程, 从而可以实现唤醒的顺序, 也提高了系统的效率.
尽管Condition对象也有内置锁, 但它的现实与之内置锁, 没有任何关系.

ReentrantLock

ReentrantLock 可以设置公平性(fair) 参数. 同时也提供了一些有用的方法去监控当前Lock的状态, 如: isLocked(), getLockQueueLength(),isHeldByCurrentThread(), getHoldCount(),getWaitingThreads(Condition condition) 等.

ReadWriteLock

读写锁允许多线程对共享资源的并发访问, 它有读锁和写锁两把锁, 读锁允许同时有多个线程读, 不能有写锁被Lock, 写锁只允许同时有且仅有一个写操作获得锁.
数据库表的操作是一个典型的读写锁的例子.
读写锁的性能取决于读操作和写操作的频率对比, 以及读操作写操作的单词操作时长.

Java Thread

创建线程

继承(extends) Thread类, 或者实现(implements) Runnable 接口, 然后实现public void run() {} 方法.
每个线程都有一个唯一的正整数 (long) 作为线程ID, 此ID在线程存活期间保持不变, 不同线程可以有相同的name

线程的启动, 停止, 暂停, 继续

启动线程, 调用 start() 方法.
Thread 的 stop(), suspend(), resume() 方法都被废弃了, 原因及替代方法看这里: http://docs.oracle.com/javase/7/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html

线程优先级 Priority

Thread类有三个静态字段 MAX_PRIORITY, MIN_PRIORITY, NORM_PRIORITY;
如果线程实例是通过new 关键字创建出来的, 那么新线程与创建它的线程有同样的优先级.
可以通过setPriority(int newPriority) 方法改变其优先级

线程的状态

线程有6种可能的状态: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED. 这6种状态是线程的内部static Thread.State Enum所定义的.

捕获异常

由于run() 方法的声明中没有抛出任何异常, 所以任何 checked exception 都必须在run() 方法内消化掉, 对于其它的任何 unchecked exception, Throwable 都有UncaughtExceptionHandler 处理. 线程和线程组实例都可以注册UncaughtExceptionHandler. 当线程遭遇这类异常后, 退出之前, 会把线程和异常传给线程的UncaughtExceptionHandler 处理, 如果线程没有注册UncaughtExceptionHandler, 则使用线程组的UncaughtExceptionHandler 处理, 如果线程组也没有, 则使用getDefaultUncaughtExceptionHandler处理.

守护线程 Daemon Thread

setDaemon(boolean on) 设置是否为 Daemon 线程, 并且必须在线程start之前设置.
当所有的非 Daemon 线程结束之后, java 程序自动退出. 当任何一个线程执行System.exit() 指令之后, 所有线程停止运行, 并退出.

线程组 ThreadGroup

线程组包含一组线程,并且可以包含其它线程组, 也就是说可以嵌套,形成树形结构. 线程组的 interrupt()方法可以对一组线程同时发送interrupt信号.

interrupt a thread

你可以通过线程的 interrupt() 方法向线程发送 interrupt 信号.
如果该线程因调用Object 的wait()方法, Thread的sleep(), join()方法而阻塞, 同时又收到 interrupt signal的时候, 这些方法会收到一个 InterruptedException.
interrupted() 方法: 返回当前interrput 状态, 并清除interrput 状态
isInterrrupt() 方法: 只返回当前interrupt状态, 不改变其值.
更多关于收到interrupt 信号后的状态设置及变化, 参看JDK官方文档.

调用某线程的 join() 方法

A线程代码中执行B.join()方法, 会使A线程停止执行, 在此处等待B线程结束. 带有参数的 join(long, int) 会等待特定的时间. join的内部实现其实是使用Object的wait() 方法.

线程 sleep() 方法 和 Object 的wait(), notify(), notifyAll() 方法

sleep() 是静态(static)方法, 它使当前线程暂停执行特定的时间. 暂停期间并不会失去任何已占有的锁(monitor).
yield() 方法只是提示线程管理者: 我可以暂停我使用CPU, 但不一定被理会.
Object的wait() 方法会使当前线程暂停(或暂停特定时间), 但是在调用wait() 方法之前, 该线程必须获得该对象的锁 (monitor), 当开始暂停时, 释放该锁, 等待获得该锁的其他线程调用该对象的 notify 或 notifyAll 方法 (或暂停特定时间后再次获得该对象的锁), 当再次获得该对象的 monitor 之后, 继续运行. 所以一般的调用方式为:
synchronized (obj) {
while ()
obj.wait();
... // Perform action appropriate to condition
}
notify 方法也一样, 必须获得monitor 之后, 才能去notify 其它线程.

ThreadLocal 变量

ThreadLocal 对于使用它的每个线程, 创建一个线程都有的实例, 凡是同一个线程去取(get), 或去修改(set) 都是这个线程唯一的实例. 所以不存在多线程竟然(race) 读写的情况. 一般情况下ThreadLocal 变量都是声明为private static 字段.
与之向类似的用于多线程编程的有个 ThreadLocalRandom 类.

Java thread 线程的6种状态

public static enum State {
/** * A Thread which has not yet started.
/
NEW,
/
* * A Thread which is running or suspended.
/
RUNNABLE,
/
* * A Thread which is blocked on a monitor.
/
BLOCKED,
/
* * A Thread which is waiting with no timeout.
/
WAITING,
/
* * A Thread which is waiting with a timeout.
/
TIMED_WAITING,
/
* * A thread which is no longer alive.
*/
TERMINATED }