对于java程序员来说,语言(java),操作系统,TCP/ip,数据结构与算法,属于比较重要的内容,打好基础,无论在工作上还是求职中,都能有很好的优势。本文继续对Java基础知识学习做笔记(参考自网上前辈的总结和文章,均在文末给出链接)。
Switch可以使用string做参数。Switch支持的类型:可以自动转换为整型的(byte,short,int),String类型,枚举类型。Java中不能做为Switch参数的有boolean,float,double,long(不能直接转换为int,需要强转)。
hashcode方法返回该对象的哈希码值。 hashCode 的常规协定是: 在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。 如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。 以下情况不是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,为不相等的对象生成不同整数结果可以提高哈希表的性能。 实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的) 当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。 1、hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的; 2、如果两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode一定要相同; 3、如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点; 4、两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”。 hashCode是用于查找使用的,而equals是用于比较两个对象的是否相等的。
总的来说,Java中的集合(Collection)有两类,一类 是List,再有一类是Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?这就是Object.equals方法了。但是,如果每增加一个元素就检查一 次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它 就要调用1000次equals方法。这显然会大大降低效率。 于是,Java采用了哈希表的原理。哈希算法也称为散列算法,是 将数据依特定算法直接指定到一个地址上。这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以 直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散 列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。 所以,Java对于eqauls方法和hashCode方法是这样规定的:
如果两个对象相同,那么它们的hashCode值一定要相同; 如果两个对象的hashCode相同,它们并不一定相同HashMap不是线程安全的,ConcurrentHashMap是线程安全的。ConcurrentHashMap是使用了锁分段技术技术来保证线程安全的。 首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
多态:同一个类的不同表现形态,不同的形态是通过其不同的子类体现 java通过将子类对象引用赋值给超类对象变量, 来实现动态方法调用。 如果类B是类A的子类, A a = new B() 编译时变量和运行时变量不一样,是多态的一个例子。
A a 作为一个引用类型数据,存储在JVM栈的本地变量表中。 new B()作为实例对象数据存储在堆中 ,B的对象实例数据(接口、方法、field、对象类型等)的地址也存储在堆中 。B的对象的类型数据(对象实例数据的地址所执行的数据)存储在方法区中,方法区中对象类型数据中有一个指向该类方法的方法表。首先虚拟机通过reference类型(A的引用)查询java栈中的本地变量表,得到堆中的对象类型数据的地址,从而找到方法区中的对象类型数据(B的对象类型数据),然后查询方法表定位到实际类(B类)的方法运行。场景:如果你向一个变量写值,而这个变量接下来可能会被另一个线程所读取,或者你从一个变量读值,而它的值可能是前面由另一个线程写入的,此时你就必须使用同步。
sychronized我在前几篇笔记中提到过了。lock Lock 接口实现提供了锁定操作。可以支持多个相关的 Condition 对象。 Condition 接口将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。 Lock 接口的实现允许锁在不同的作用范围内获取和释放,并允许以任何顺序获取和释放多个锁。 在大多数情况下,应该使用以下语句:
Lock l = ...; //lock接口的实现类对象 l.lock(); try { // access the resource protected by this lock } finally { l.unlock(); }在java.util.concurrent.locks包中有很多Lock的实现类,常用的有ReentrantLock、ReadWriteLock(实现类ReentrantReadWriteLock).它们是具体实现类,不是 java语言关键字。
ReentrantLock 一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义。
class X { private final ReentrantLock lock = new ReentrantLock(); // ... public void m() { lock.lock(); // block until condition holds try { // ... method body } finally { lock.unlock() } } }重入性:指的是同一个线程多次试图获取它所占有的锁,请求会成功。当释放锁的时候,直到重入次数清零,锁才释放完毕。 ReentrantLock 的lock机制有2种,忽略中断锁和响应中断锁。比如:如果A、B 2个线程去竞争锁,A线程得到了锁,B线程等待,但是A线程这个时候实在有太多事情要处理,就是 一直不返回,B线程可能就会等不及了,想中断自己,不再等待这个锁了,转而处理其他事情。这个时候ReentrantLock就提供了2种机制,第一,B线程中断自己(或者别的线程中断它),但是ReentrantLock 不去响应,继续让B线程等待,你再怎么中断,我全当耳边风;第二,B线程中断自己(或者别的线程中断它),ReentrantLock 处理了这个中断,并且不再等待这个锁的到来,完全放弃。 ReentrantLock相对于synchronized多了三个高级功能: (1)等待可中断:在持有锁的线程长时间不释放锁的时候,等待的线程可以选择放弃等待. (2)公平锁:按照申请锁的顺序来一次获得锁称为公平锁.synchronized的是非公平锁,ReentrantLock可以通过构造函数实现公平锁. 公平锁和非公平锁:这2种机制的意思从字面上也能了解个大概:即对于多线程来说,公平锁会依赖线程进来的顺序,后进来的线程后获得锁。而非公平锁的意思就是后进来的锁也可以和前边等待锁的线程同时竞争锁资源。对于效率来讲,当然是非公平锁效率更高,因为公平锁还要判断是不是线程队列的第一个才会让线程获得锁。 (3)绑定多个Condition:通过多次new Condition可以获得多个Condition对象,可以简单的实现比较复杂的线程同步的功能.通过await(),signal();
synchronized和lock的用法与区别 synchronized是托管给JVM执行的,而lock是java写的控制锁的代码。 synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁。独占锁意味着其他线程只能依靠阻塞来等待线程释放锁。而在CPU转换线程阻塞时会引起线程上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低。 Lock用的是乐观锁方式。每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。 ReentrantLock必须在finally中释放锁,否则后果很严重,编码角度来说使用synchronized更加简单,不容易遗漏或者出错。 ReentrantLock提供了可轮询的锁请求,他可以尝试的去取得锁,如果取得成功则继续处理,取得不成功,可以等下次运行的时候处理,所以不容易产生死锁,而synchronized则一旦进入锁请求要么成功,要么一直阻塞,所以更容易产生死锁。 synchronized的话,锁的范围是整个方法或synchronized块部分;而Lock因为是方法调用,可以跨方法,灵活性更大 一般情况下都是用synchronized原语实现同步,除非下列情况使用ReentrantLock: (1)某个线程在等待一个锁的控制权的这段时间需要中断。 (2)需要分开处理一些wait-notify,ReentrantLock里面的Condition应用,能够控制notify哪个线 (3)具有公平锁功能,每个到来的线程都将排队等候
方法锁(synchronized修饰方法时) 通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。 synchronized 方法控制对类成员变量的访问: 每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态,从而有效避免了类成员变量的访问冲突。
对象锁(synchronized修饰方法或代码块) 当一个对象中有synchronized method或synchronized block的时候调用此对象的同步方法或进入其同步区域时,就必须先获得对象锁。如果此对象的对象锁已被其他调用者占用,则需要等待此锁被释放。(方法锁也是对象锁) java的所有对象都含有1个互斥锁,这个锁由JVM自动获取和释放。线程进入synchronized方法的时候获取该对象的锁,当然如果已经有线程获取了这个对象的锁,那么当前线程会等待;synchronized方法正常返回或者抛异常而终止,JVM会自动释放对象锁。这里也体现了用synchronized来加锁的1个好处,方法抛异常的时候,锁仍然可以由JVM来自动释放。
对象锁的两种形式:
public class Test{ // 对象锁:形式1(方法锁) public synchronized void Method1() { System.out.println("我是对象锁也是方法锁"); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } // 对象锁:形式2(代码块形式) public void Method2() { synchronized (this) { System.out.println("我是对象锁"); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }类锁(synchronized 修饰静态的方法或代码块) 由于一个class不论被实例化多少次,其中的静态方法和静态变量在内存中都只有一份。所以,一旦一个静态的方法被申明为synchronized。此类所有的实例化对象在调用此方法,共用同一把锁,我们称之为类锁。 对象锁是用来控制实例方法之间的同步,类锁是用来控制静态方法(或静态变量互斥体)之间的同步。 类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的。 java类可能会有很多个对象,但是只有1个Class对象,也就是说类的不同实例之间共享该类的Class对象。Class对象其实也仅仅是1个java对象,只不过有点特殊而已。由于每个java对象都有1个互斥锁,而类的静态方法是需要Class对象。所以所谓的类锁,不过是Class对象的锁而已。获取类的Class对象有好几种,最简单的就是[类名.class]的方式。public class Test{ // 类锁:形式1 public static synchronized void Method1() { System.out.println("我是类锁一号"); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } // 类锁:形式2 public void Method2() { synchronized (Test.class) { System.out.println("我是类锁二号"); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }}生产者消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一存储空间,生产者向空间里生产数据,而消费者取走数据。 好理解的例子 生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。 生产者消费者模式的优点: - 解耦 - 支持并发 - 支持忙闲不均
主要是从网上前辈的文章中摘录出的知识点,供自己学习使用,希望可以有所帮助。
面试心得与总结—BAT、网易、蘑菇街 Switch能否使用String做参数 Object有哪些公用方法? Hashcode的作用 HashMap和ConcurrentHashMap分享 多态实现的JVM调用过程 线程同步的方法:sychronized、lock、reentrantLock分析 方法锁、对象锁和类锁的意义和区别 生产者—消费者模型 多线程下生产者消费者问题的五种同步方法实现 Java线程:并发协作-生产者消费者模型
新闻热点
疑难解答