以下:内容主要来源《JAVA核心技术》。部分内容有参考已标注,如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正。
一.多线程
1. 单独调用Thread或者Runnable对象的run方法并不会开辟一个新线程,仍会在当前线程中运行。开辟新线程需要通过调用start方法
2. 线程存在 新生、可运行、阻塞、死亡四种状态。
新生指new出来线程对象但是还没有开始运行。
可运行指线程对象调用了start方法。注意可运行状态的线程可能运行了也可能没有运行,这取决于操作系统的分配。
阻塞线程的情况可能是: 调用sleep方法进入睡眠时间,线程执行IO操作时被阻塞。线程获取线程锁时,而锁被其它线程持有。线程等待某个触发条件。线程suspend方法被调用(suspend已过时,与之对应的resume也已过时)
3. 无法判断一个活着的线程是可运行的还是被阻塞的,也无法确定一个可运行的线程是否正在运行。无法区分死亡线程和非可运行线程。
4. 通过调用setPriority方法可以降低或者提高线程的优先级,从0-10,从最低到最高,NORM_PRIORITY 被定义为5。需要注意的是,当有一些高优先级线程很少发生阻塞时可能会导致一些低优先级的永远无法执行,即线程饿死(ReentrantLock公平锁可以避免这种情况)。
5. 线程的优先级是高度依赖于系统的,不同系统平台上的优先级并不相同。
6. 可以通过t.setDaemon(true)来将线程设置为守护线程。守护线程的唯一作用就是为其它线程提供服务(如计时器,定时为其它线程发送事时间信号)。当只剩下守护线程的时候,虚拟机就会退出。
7. 线程组:
线程组的创建方式: ThreadGroup g = new ThreadGroup(groupName);
TheradGroup gg = new ThreadGroup(g, groupName11); // 创建一个线程组的父线程组是g
线程加入线程组: Thread t = new Thread(g, threadName);
判断是否线程组中还有存活线程: g.activeCount() == 0
中断线程组中的所有线程: g.interrupt();
获取当前线程所在的线程组 : ThreadGroup g = t.getThreadGroup()
获取线程组中所有活鲜城的引用: g.enumerate(Thread[] list);
二. 线程的同步: 在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetarntLock,但是在很激烈的情况下,synchronized的性能会下降几十倍。
1. synchronized : 在JVM层面实现,不需要自己手动释放
Object :
notifyAll,notify, wait及其重载方法,可以唤醒或者使线程进入等待状态,如果当前线程不是对象锁的持有者,则会抛出异常。
2.Lock :Lock不是Java语言内置的。 在JDK中实现,需要自己手动释放ReentrantLock、ReadLock和WriteLock** ---- 参考: https://blog.csdn.net/u011521203/article/details/80186741;https://www.cnblogs.com/Wanted-Tao/p/6378942.html
ReentrantLock(重入锁)支持重入,即若线程多次锁则需要多次释放。支持公平锁和非公平锁两种模式: 公平锁即绝对公平,满足FIFO条件,非公平锁则不满足。ReentrantLoc默认非公平锁。创建ReentrantLock对象时可以通过Boolean来指定创建公平锁还是非公平锁。true为公平锁。
公平锁每次获取到锁为同步队列中的第一个节点,保证请求资源时间上的绝对顺序,而非公平锁有可能刚释放锁的线程下次继续获取 该锁,则有可能导致其他线程永远无法获取到锁,造成“饥饿”现象。
公平锁为了保证时间上的绝对顺序,需要频繁的上下文切换,而非公平锁会降低一定的上下文切换,降低性能开销。
Lock相关
a. void lock(); // 无条件的锁;
b. void lockInterruptibly throws InterruptedException;//可中断的锁;
解释: 使用ReentrantLock如果获取了锁立即返回,如果没有获取锁,当前线程处于休眠状态,直到获得锁或者当前线程可以被别的线程中断去做其他的事情;但是如果是synchronized的话,如果没有获取到锁,则会一直等待下去;
c. boolean tryLock();//如果获取了锁立即返回true,如果别的线程正持有,立即返回false,不会等待;
d. boolean tryLock(long timeout,TimeUnit unit);//如果获取了锁立即返回true,如果别的线程正持有锁,会等待参数给的时间,在等待的过程中,如果获取锁,则返回true,如果等待超时,返回false;
ReentrantLock r = new ReentrantLock(true); // 创建公平锁
try{
r.lock();
// 一系列操作
}cathch(Exception e){
}finally{
r.unlock();
}
Condition相关特性:
1.Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。不同的是,Object中的这些方法是和同步锁捆绑使用的;而Condition是需要与互斥锁/共享锁捆绑使用的。
2.Condition它更强大的地方在于:能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition。
例如,假如多线程读/写同一个缓冲区:当向缓冲区中写入数据之后,唤醒"读线程";当从缓冲区读出数据之后,唤醒"写线程";并且当缓冲区满的时候,"写线程"需要等待;当缓冲区为空时,“读线程"需要等待。 如果采用Object类中的wait(), notify(), notifyAll()实现该缓冲区,当向缓冲区写入数据之后需要唤醒"读线程"时,不可能通过notify()或notifyAll()明确的指定唤醒"读线程”,而只能通过notifyAll唤醒所有线程(但是notifyAll无法区分唤醒的线程是读线程,还是写线程)。 但是,通过Condition,就能明确的指定唤醒读线程。
例子这里只是随手写一下加深印象,详细参考:https://www.cnblogs.com/Wanted-Tao/p/6378942.html
public void main(String[] args) {
Thread t1=new Thread(new AddThread());
Thread t3=new Thread(new AddThread());
Thread t7=new Thread(new AddThread());
Thread t8=new Thread(new AddThread());
Thread t2 = new Thread(new SubThread());
Thread t4 = new Thread(new SubThread());
Thread t5 = new Thread(new SubThread());
Thread t6 = new Thread(new SubThread());
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
t7.start();
t8.start();
}
public void add() {
try {
lock.lock();
if (list.size() == 10) {
addCondition.await(); // 当前线程注册到增加条件等待集中,且开始等待
}
list.add("666");
subCondition.signal(); // 随机唤醒一个减少条件等待集中的一个减线程
subCondition.signalAll(); // 唤醒减少条件等待集中的所有减线程
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
lock.unlock();
}
public void sub() {
try {
lock.lock();
if (list.size() == 0) {
subCondition.await(); // 当前线程注册到增加条件等待集中,且开始等待
}
list.remove(0);
addCondition.signal(); // 随机唤醒一个增加条件等待集中的一个增线程
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public class AddThread implements Runnable{
@Override
public void run() {
add();
}
}
public class SubThread implements Runnable{
@Override
public void run() {
sub();
}
}
-
volatile 关键字 ---- volatile 保证可见性只能保证每次读取的是最新的值,但是volatile没办法保证对变量的操作的原子性 : https://www.cnblogs.com/dolphin0520/p/3920373.html
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
2)禁止进行指令重排序。 -
阻塞队列(线程安全) : http://ifeve.com/java-blocking-queue/
LinkedBlockingQueue : 默认容量无上限,也可以指定最大容量
ArrayBlockingQueue : 需要在构造的时候指定容量,并可以选择是否公平性,如果公平性被选择,等待时间最长的线程会得到优先处理。
PriorityBlockingQueue : 携带优先级,并不遵循先入先出规则,芫荽按照优先级被移除,容量没有上线。add 增加一个元素,队列已满抛出异常lllegalStateException
remove 移除并返回队列头部元素,队列为空,抛出异常NoSuchElementException
element 返回队列头部元素,队列为空,抛出异常NoSuchElementException
offer 添加一个元素并返回true,队列满返回false (还具有带有超时的重载方法)
poll 移除并返回队列头部元素,队列空返回null (还具有带有超时的重载方法)
peek 返回队列头部元素,队列空返回null
put 添加一个元素,队列满,则阻塞
take 移除并返回队列头部元素,队列空,则阻塞
注: ConcurrentLinkedQueue 和 ConcurrentHashMap 线程安全的同时实现了高效.
ConcurrentHashMap 中存在方法 putIfAbsent(key, value) 方法。即增加一个未被建立过的新的关联。可以保证多个线程不会插入同一条条目到缓存中。
-
线程池:https://www.cnblogs.com/aaron911/p/6213808.html
-
线程的同步器: 障栅 CyclicBarrier。 倒计时门栓(不再叙述。。。。好累)
CyclicBarrier : 当所有线程都准备好时(执行到指定步,到达锁设置的障栅时),障栅就撤销,所有线程可以继续运行。
每个线程都做了一些工作,完成后再障栅上调用await
public void run(){
doWork();
barrier.await();
}
需要注意的是: 如果任意一个在障栅处等待的线程离开了,障栅则会被破坏,所有其他线程await方法则会跑出BrokenBarrierException方法。
先写这么多。。。。。。