第一章
1. isInterrupted()和interrupt()的区别
public boolean isInterrupted() { return isInterrupted(false); } public static boolean interrupted() { return currentThread().isInterrupted(true); } /** * Tests if some Thread has been interrupted. The interrupted state * is reset or not based on the value of ClearInterrupted that is * passed. */ private native boolean isInterrupted(boolean ClearInterrupted);
interrupted() 判断当前线程是否有中断标记,并清除中断标记。(jdk的命名也可读性也不好)
2. Thread.yield()方法
使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。
yield()的作用是放弃当前的cpu资源,将它让给其他的任务去占用cpu执行时间,但放弃的时间不确定,有可能刚刚放弃马上就获得了时间片。
3. 线程的优先级setPriority(int newPriority)
优先级高的分配的CPU资源会多点
4. 守护线程
5. 停止线程
可以用interrupt()和renturn结合使用
第二章
1.synchronized 同步语句
synchronized可以重入不可以继承
synchronized同步方法 和 synchronized(this) 用this对象作为“对象监视器”来实现同步功能。
static synchronized同步方法 和 synchronized(class) 用class类作为“对象监视器”来实现同步功能。
当我们用synchronized(**)来同步时,要注意避免用字符串来作为“对象监视器”(详情见102页)
2.volatile
volatile修饰变量,保证在多线程中变量的可见性(这句话可以理解为在一个方法里面每次去取volatile修饰的变量时都是在主内存去取而不是在私有栈去取)
下面2个图,一个是非volatile修饰,一个是有volatile修饰(很好理解吧)
下面的代码我觉得是最能解释volatile的可见性。
isRunning初始化为true
如果isRunning没有被volatile修饰,当另一个线程修改isRunning为false时,下图还一直循环(因为isRunning取的还是方法栈里面的变量值)
如果isRunning被volatile修饰,当另一个线程修改isRunning为false时,下图会打印“线程被停止了!”(因为isRunning取的还是主内存的值)
但是要记住,volatile不能保证同步,意思是不能保证原子性。
第三章(线程间通信)
1.线程是独立的,如果让多个线程之间有联系可以用wait()和notify()来通信,
当线程要用wait()和notify(),必须要先获取对象锁,才能调用对象的await()和notify()方法。否则会出现java.lang.IllegalMonitorStateException异常。
2.当一个线程执行wait()之后,线程会停止运行,只能等待其他线程去notify(). (这个不是绝对的,见下面解释)
当另一个线程执行notify()时,当前线程并不会马上释放锁,只是随机挑选出其中一个wait()的线程,对其发送notify通知,等notify线程执行完同步方法后释放锁。
await状态的线程才能获得锁去执行代码。
3.当线程呈wati()状态时,调用线程的interrupt()方法时会出现interruptedException异常
4.带有参数的awati(long)方法,指如果在long时间内没有被其他线程notify唤醒时,会自己自动唤醒。
5.wait()方法只能由其他线程的notify去唤醒吗,NO!
那在什么情况下wait()方法会被唤醒呢,当调用wait()的对象是Thread时,如果这个线程对象在wait之前isAlive是true的时候,wait()会阻塞,
如果阻塞期间这个线程对象isAlive变为false的时候,wait()会别唤醒。看看下面代码。
当把Thread.sleep(2000);注释代码打开的时候,wait()会被唤醒。
public class MainThread { public static void main(String[] args) throws InterruptedException { SubThread sub = new SubThread(); System.out.println("MainThread----子线程状态=" +sub.getState() + " isAlive=" + sub.isAlive()); sub.start(); Thread.sleep(1000); synchronized(sub) { try{ System.out.println("MainThread----子线程状态=" +sub.getState() + " isAlive=" + sub.isAlive()); sub.wait(); System.out.println("MainThread----子线程状态=" +sub.getState() + " isAlive=" + sub.isAlive()); System.out.println("MainThread----执行结束"); } catch(InterruptedException e){ System.out.println("e" + e); } } } public static class SubThread extends Thread { @Override public void run() { try { System.out.println("SubThread------开始 状态" +getState() + " isAlive=" + isAlive()); // Thread.sleep(2000); System.out.println("SubThread--------结束"); } catch (Exception ex) {} } } }
6. 线程的join方法就是上面的原理,(注意:join是线程的方法,wait和notify是Object的方法)
join方法:使所属的线程对象X正常执行run方法,而是当前线程Z进行无限期的阻塞,等待线程X执行完run方法销毁后再执行Z以后的代码。
在join过程中,如果当前线程Z被中断,则Z线程出现异常。
join和sleep的区别:因为join的原理是通过先锁住线程后调用wait方法实现的,所以它释放了线程的锁,而sleep不会释放锁
如下图,b.join的时候通过wait就释放了监听对象b。
第四章(ReentrantLock)
1.要实现同步除了可以用synchronized外还可以用RenntrantLock去实现;
用法就是lock.lock()和lock.unlock(); 其实最底层实现就是通过LockSupport的pack()和unpark(Thread)方法。
(知识点:当一个A线程park阻塞时,别的线程B除了可以通过unpark(A)去唤醒A,而且可以通过调用A.interrupt()去唤醒A并且不会出现InterruptedException异常)
2.RenntrantLock类里通过组合AbstractQueuedSynchronizer(AQS)去实现同步。
RenntrantLock类里有2个AQS(公平和不公平)可提供使用,通过构造器去决定使用那个具体的AQS,默认是不公平的。
2个AQS区别就是
公平AQS去lock时要判断队列里面是否有等待的线程,如果有的话就加入到队列最后,
非公平AQS去lock的时候不用判断队列里是否有线程等待,直接去竞争。如果失败了就加入到队列中去以后就是公平了。
下面是2个AQS分别获取lock的过程;
FairSync.lock()
final void lock() { acquire(1); } public final void acquire(int arg) { //tryAcquire尝试获取锁,如果失败就addWaiter添加到线程队列, //acquireQueued会再次去获取锁,如果失败会通过LockSupport.park阻塞 等待队列前一个线程通过unpark去唤醒它 //selfInterrupt的作用是(以后再说) if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } }
NonfairSync.lock()
final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } /** * 其中非公平锁acquire过程中就tryAcquire跟公平锁不一样 * 就是上面去掉黄色代码 */
详情细节可以参考http://www.cnblogs.com/skywang12345/p/3496147.html
http://www.cnblogs.com/waterystone/p/4920797.html
3.AQS原理简单描述
AQS有个Node节点类,有头有尾形成链表结构wait queue
那这个链表里面的数据是什么呢,就是当线程去通过lock.lock()时没有获取到锁时,把这个没有获取到锁的线程加入到链表尾部。
初始化的时候有4个线程0,1,2,3 当第一个线程0获取到锁。 链表结构组成是 (head)new node()-->等待线程1-->等待线程2-->(tail)等待线程3.
当线程0 调用unlock时,会唤醒头部的下个节点head.next() 即等待线程1。当线程1获取到锁的时候 会把自己设置为头部,同时清理它之前的头部节点即上面的new node().
这时链表结构变成:(head)线程1-->等待线程2-->(tail)等待线程3.
同时AQS里面有ConditionObject类,那这个类是干嘛的呢, 这个类不仅有类似object.wait 和 notify的作用。
同时还有指定唤醒哪个线程的作用(一个lock可以有多个condition,一个condition.signal()只能唤醒跟他相同condition.await()的线程,不同condition之间不能唤醒)
ConditionObject类里也有个链表结构condition queue。
当获取到锁的线程A调用condition.await()时会创建一个节点加入到condition queue里面;会释放锁同时线程会阻塞。
如果这个时候另外一个线程B获取锁并通过condition.signal()会立刻唤醒A吗,答案是不一定。
因为这时如果wait queue里面有别的等待线程C,D..的话的.就不会马上唤醒A,因为condition.signal()会把线程A加入到wait queue的尾部
4.ReentrantReadWriteLock介绍
ReentrantReadWriteLock是读写锁,作用是
2个线程分别是写锁与写锁,互斥
2个线程分别是读锁与写锁,互斥
2个线程分别是写锁与读锁,互斥(这里要注意下,写入线程获取锁的同时可以再获取读取锁)
2个线程分别是读锁与读锁,异步,非互斥。
独立锁ReentrantLock 通过一个state字段区别锁是否被占用了,那读写锁怎么弄呢?,读锁和写锁应该由2个字段来判断占用的锁是读还是写吧。
显然现在一个state就不够用了。于是在ReentrantReadWrilteLock里面将这个字段一分为二,高位16位表示共享锁的数量,低位16位表示独占锁的数量(或者重入数量)
exclusiveCount是指写锁的数量,sharedCount是读锁的数量。
这里有位运算符,
x>>>y 表示 x向右移动多少位
x<<y 表示 x向左移动多少位
x & y 表示 x二进制和y二进制 相同的位才合并,比如00010001 & 11111001 = 00010001
/* * Read vs write count extraction constants and functions. * Lock state is logically divided into two unsigned shorts: * The lower one representing the exclusive (writer) lock hold count, * and the upper the shared (reader) hold count. */ static final int SHARED_SHIFT = 16; static final int SHARED_UNIT = (1 << SHARED_SHIFT); static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1; static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; /** Returns the number of shared holds represented in count */ static int sharedCount(int c) { return c >>> SHARED_SHIFT; } /** Returns the number of exclusive holds represented in count */ static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
c >>> 16 右移16位, int 是32位,右移16位就是取前16位的值,
c & 2的16次 - 1 相对于c & 01111111 11111111 就是取后16位的值,