摘要:需要注意的是对于方法或者代码块,当出现异常时,会自动释放当前线程占用的锁,因此不会由于异常导致出现死锁现象。用于实现线程间的通信,它是为了解决难以使用的问题。
速度StringBuilder>StringBuffer>String,StringBuffer线程安全
线程安全的集合有:Vector、Stack、HashTable、ConcurrentHashMap、
CopyOnWriteXXX(如CopyOnWriteArrayList)
ClassLoader
程序在启动的时候,并不会一次性加载程序所要用的所有class文件,而是根据程序的需要,通过Java的类加载机制(ClassLoader)来动态加载某个class文件到内存当中的,从而只有class文件被载入到了内存之后,才能被其它class所引用。所以ClassLoader就是用来动态加载class文件到内存当中用的。Java默认提供的三个ClassLoader。BootStrap ClassLoader:称为启动类加载器,是Java类加载层次中最顶层的类加载器,负责加载JDK中的核心类库,如:rt.jar、resources.jar、charsets.jar等。
Extension ClassLoader:称为扩展类加载器,负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目下的所有jar。
App ClassLoader:称为系统类加载器,负责加载应用程序classpath目录下的所有jar和class文件。
ClassLoader使用的是双亲委托模型来搜索类的,每个ClassLoader实例都有一个父类加载器的引用(不是继承的关系,是一个包含的关系),虚拟机内置的类加载器(Bootstrap ClassLoader)本身没有父类加载器,但可以用作其它ClassLoader实例的的父类加载器。
JVM在判定两个class是否相同时,不仅要判断两个类名是否相同,而且要判断是否由同一个类加载器实例加载的。只有两者同时满足的情况下,JVM才认为这两个class是相同的
Java ClassLoader原理
synchronized lock
浅谈Java并发编程系列(五)Java并发编程之线程同步
当一个线程正在访问一个对象的synchronized方法,那么其他线程不能访问该对象的其他synchronized方法,因为一个对象只有一把锁,当一个线程获取了该对象的锁之后,其他线程无法获取该对象的锁,所有无法访问该对象的其他synchronized方法。
当一个线程正在访问一个对象的synchronized方法,那么其他线程能访问该对象的非synchronized方法。因为非synchronized方法不需要获取该对象的锁。
如果一个线程A需要访问对象object1的synchronized方法fun1,另外一个线程B需要访问对象object2的synchronized方法fun1,即使object1和object2是同一类型,也不会产生线程安全问题,因为他们访问的是不同的对象,所以不存在互斥问题。
如果一个线程执行一个对象的非static synchronized方法,另一个线程执行这个对象所属类的static synchronized方法,此时不会发生互斥现象,因为访问static synchronized方法占用的是类锁,而访问非static synchronized方法占用的是对象锁,所以不存在互斥现象。
需要注意的是:对于synchronized方法或者synchronized代码块,当出现异常时,JVM会自动释放当前线程占用的锁,因此不会由于异常导致出现死锁现象。
显式锁ReentrantLock则可以将锁的获得和释放分开。同时显式锁可以提供轮训锁和定时锁,同时可以提供公平锁或者非公平锁。
ReentrantLock和Condition的关系
在ReentrantLock类中有一个重要的函数newCondition(),该函数用于获取lock上的一个条件,也就是说Condition是和Lock绑定的。Condition用于实现线程间的通信,它是为了解决Object.wait()、notify()、notifyAll()难以使用的问题。
public class MyArrayBlockingQueue{ // 数据数组 private final T[] items; // 锁 private final Lock mLock = new ReentrantLock(); // 数组满的条件 private Condition notFull = mLock.newCondition(); // 数组空的条件 private Condition notEmpty = mLock.newCondition(); // 头部 private int head; // 尾部 private int tail; // 数据数量 private int count; public MyArrayBlockingQueue(int maxSize) { // TODO Auto-generated constructor stub items = (T[]) new Object[maxSize]; } public MyArrayBlockingQueue() { // TODO Auto-generated constructor stub this(10); } public void put(T t) { mLock.lock(); try { // 如果数据已满,等待 while (count == getCapacity()) { System.out.println("数据已满,请等待"); notFull.await(); } System.out.println("存入数据"); items[tail] = t; if (++tail == getCapacity()) { tail = 0; } ++count; // 唤醒等待数据的线程 notEmpty.signalAll(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { mLock.unlock(); } } public T take() { mLock.lock(); try { // 如果数组数据为空,则阻塞 while (count == 0) { System.out.println("还没有数据,等待"); notEmpty.await(); } System.out.println("取出数据"); T t = items[head]; items[head] = null; if (++head == getCapacity()) { head = 0; } --count; // 唤醒添加数据的线程 notFull.signalAll(); return t; } catch (InterruptedException e) { // TODO: handle exception } finally { mLock.unlock(); } return null; } public int getCapacity() { return items.length; } public int size() { mLock.lock(); try { return count; } finally { mLock.unlock(); } } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub final MyArrayBlockingQueue mQueue = new MyArrayBlockingQueue<>( 5); new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub while (true) { for(int i = 0;i < 3;i++) mQueue.put("just"); try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub while (true) { mQueue.take(); } } }).start(); } } 结果打印 存入数据 存入数据 存入数据 取出数据 取出数据 取出数据 还没有数据,等待 存入数据 存入数据 存入数据 取出数据 取出数据 取出数据 还没有数据,等待
当时看到这段代码我就想到一个问题:如果一个线程lock()对象后被挂起还没有unlock,那么另外一个线程就拿不到锁了(lock()操作会挂起),那么就无法通知(notify)前一个线程,这样岂不是“死锁”了?再回头看代码,不管take()还是put(),在进入lock.lock()后唯一可能释放锁的操作就是await()了。也就是说await()操作实际上就是释放锁,然后挂起线程,一旦条件满足就被唤醒,再次获取锁!
await源码如下
public final void await() throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); Node node = addConditionWaiter(); int savedState = fullyRelease(node); int interruptMode = 0; while (!isOnSyncQueue(node)) { LockSupport.park(this); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; } if (acquireQueued(node, savedState) && interruptMode != THROW_IE) interruptMode = REINTERRUPT; if (node.nextWaiter != null) unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode); }
完整的await()操作是安装如下步骤进行的:
将当前线程加入Condition锁队列。特别说明的是,这里不同于AQS的队列,这里进入的是Condition的FIFO队列。后面会具体谈到此结构。进行2。
释放锁。这里可以看到将锁释放了,否则别的线程就无法拿到锁而发生死锁。进行3。
自旋(while)挂起,直到被唤醒或者超时或者CACELLED等。进行4。
获取锁(acquireQueued)。并将自己从Condition的FIFO队列中释放,表明自己不再需要锁(我已经拿到锁了)。
介绍Condition的数据结构。我们知道一个Condition可以在多个地方被await*(),那么就需要一个FIFO的结构将这些Condition串联起来,然后根据需要唤醒一个或者多个(通常是所有)。所以在Condition内部就需要一个FIFO的队列。
//conditon 的两个属性 private transient Node firstWaiter; private transient Node lastWaiter;
这两个节点就是描述一个FIFO的队列。我们再结合前面提到的节点(Node)数据结构。我们就发现Node.nextWaiter就派上用场了!nextWaiter就是将一系列的Condition.await*串联起来组成一个FIFO的队列。
wait和sleep的区别
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/70373.html
摘要:,看到很多文章把理解为暂停,其实是不准确的,翻译过来用让步更为准确一些。如下在中我们可以看到官方的解释,更多的是作为调试或测试时候使用也就是疯狂重分配,提高切换概率,模拟并发 yield,看到很多文章把yield理解为暂停,其实是不准确的,翻译过来用让步更为准确一些。简单描述下其作用: 使调用yield的正在执行的线程让出cpu,让同等优先权的其他线程包括自身重新进行分配调度 概念性的...
摘要:问题描述遇到一个题目经过强制类型转换以后,变量,的值分别为多少答案是这涉及到的,打算借此稍稍研究一下。分为两种,一是扩展型基本数据类型转换,二是窄化型基本数据类型转换。需要注意的是是有可能丢失数值的整体信息以及损失精度和范围的。 问题描述 遇到一个题目: 经过强制类型转换以后,变量a,b的值分别为多少? short a = 128; byte b = (byte) a;a = ?,...
摘要:正如我标题所说,简历被拒。看了我简历之后说头条竞争激烈,我背景不够,点到为止。。三准备面试其实从三月份投递简历开始准备面试到四月份收,也不过个月的时间,但这都是建立在我过去一年的积累啊。 本文是 无精疯 同学投稿的面试经历 关注微信公众号:进击的java程序员K,即可获取最新BAT面试资料一份 在此感谢 无精疯 同学的分享 目录: 印象中的头条 面试背景 准备面试 ...
摘要:正如我标题所说,简历被拒。看了我简历之后说头条竞争激烈,我背景不够,点到为止。。三准备面试其实从三月份投递简历开始准备面试到四月份收,也不过个月的时间,但这都是建立在我过去一年的积累啊。 本文是 无精疯 同学投稿的面试经历 关注微信公众号:进击的java程序员K,即可获取最新BAT面试资料一份 在此感谢 无精疯 同学的分享目录:印象中的头条面试背景准备面试头条一面(Java+项目)头条...
摘要:代码实现在控制台打印总结本篇文章带大家搭好环境,并体验了控制台打印。输出结果总结熟练掌握取余和整除运算,大有作用。终止本次循环,继续执行下一次循环。 ?本文收录...
阅读 2941·2023-04-25 17:22
阅读 1489·2019-08-30 15:54
阅读 1240·2019-08-30 15:53
阅读 1750·2019-08-30 15:43
阅读 2925·2019-08-29 12:29
阅读 1174·2019-08-26 11:37
阅读 3211·2019-08-23 18:02
阅读 1574·2019-08-23 14:15