java学习整理(线程,集合)

多线程

Java创建线程的四种方式

1、继承Thread类,实现run()方法。

2、实现Runnable接口,实现run()方法。Runnable实例对象作为Thread构造方法中的target参数传入,充当线程执行体。这种方式适用于多个线程共享资源的情况。

3、实现Callable<Class>接口,实现call()方法。

call()方法与run()方法的两个主要区别:

1、call()方法可以有返回值,且返回值类型需要与声明接口时的泛型类型一致。

2、call()方法可以抛出异常。

实现Callable接口通过FutureTask包装器来创建Thread线程
4 使用ExecutorService、Callable、Future实现有返回结果的线程

Java终止线程的四种方式

1、自然终止,run()方法执行结束后,线程自动终止。

2、使用stop()方法,已经被弃用。

3、使用volatile 标志位(其实就是外部控制的自然死亡)。

4、使用interrupt()方法中断运行态和阻塞态线程。(关于interrupt(),调用interrupt()方法,立刻改变的是中断状态,但如果不是在阻塞态,就不会抛出异常;如果在进入阻塞态后,中断状态为已中断,就会立刻抛出异常。但是在获取synchronized锁的过程中不可被中断。

怎么唤醒一个阻塞的线程

如果线程是因为调用了wait()、sleep()或者join()方法而导致的阻塞,可以中断线程,并且通过抛出InterruptedException来唤醒它;如果线程遇到了IO阻塞,无能为力,因为IO是操作系统实现的,Java代码并没有办法直接接触到操作系统。

简述线程的生命周期

1、新建态,通过上述几种方式创建了具有线程执行体的Thread对象,就进入了新建态。

2、就绪态,调用Thread对象的start()方法,就会为线程分配线程私有的方法栈、程序计数器资源,如果得到CPU资源,线程就会由就绪态转为运行态。换句话说,就绪态的线程获得了除CPU之外的所有必须资源。

3、运行态,就绪态线程得到CPU资源就会转为运行态,执行run()方法。当然,在调用yield()线程让步的情况,线程会由运行态转到就绪态,但这个过程可能是及其短暂的,如果当前线程拥有较高的优先级,即使让步后,它也会直接转为运行态。

4、阻塞态,会导致阻塞态的方法主要有:sleep()方法、wait()方法、join()方法、等待获取锁、等待IO等情况。在这些情况被处理后,就会转为就绪态,等待调度。

5、终止态,包括第四个问题所说的几种情况。

start()方法与run()方法区别

start()方法用于启动线程会把线程状态由新建态转为就绪态,为线程分配线程私有的方法栈、程序计数器等资源,而start()方法会自动把run()方法作为线程执行体

run()方法本身与普通方法并无二致,直接调用run()方法不会具有线程的意义(不会产生新的线程)。

sleep()方法与wait()方法区别是什么?

它们的都是使线程“等待”一段时间,但是:

sleep()方法是Thread类下的方法,控制线程休眠,休眠过程中不会释放锁,sleep()时间到后进入就绪状态等待调度。

wait()方法是Object类下的方法,控制线程等待,等待过程会释放锁,被notify()/notifyAll()后会进入就绪状态等待调度。

wait()方法为什么属于Object类?

(为什么wait(),notify(),notifyAll()在对象中,而不在Thread类中)

java中锁的级别是对象级而不是线程级,每个对象都有锁,通过线程获得。如果wait()方法在线程中,线程正在等待的是哪个锁就不明显了。

至于wait()方法为什么属于Object类,而不是Thread类,是因为:

wait()方法用于多个线程争用一把锁(一般为Synchronized锁住的对象)的情况,同一时刻只有一个线程能够获得锁,其他线程就要在线程队列等待。作用对象就是被锁住的对象,所以线程队列的维护工作应该交给Object。如果交给Thread,那么每个Thread都要知道其他Thread的状态,这并不合理。

什么是线程池

线程池是管理多线程的一种工具,一个进程可以创建的线程数量是有限的,通过线程池创建的线程执行完成一个任务后不会立即关闭,而是继续执行阻塞队列中的线程直到阻塞线程为空。

.如何避免死锁

死锁发生的条件是
1.存在循环等待
2.存在资源竞争
3.不剥夺条件,已经获得的资源不会被剥夺
4.请求与保持,一个线程因请求资源被阻塞时,拥有资源的线程的状态不会改变。
避免死锁只需要破环其中的一个条件就可以了。

写出3条你遵循的多线程最佳实践

  • 多用同步类,少用wait,notify
  • 少用锁,应当缩小同步范围
  • 给线程一个自己的名字
  • 多用并发集合少用同步集合

Java常见的锁类型有哪些?请简述其特点。

1、synchronized对象同步锁:synchronized是对对象加锁,可作用于对象、方法(相当于对this对象加锁)、静态方法(相当于对Class实例对象加锁,锁住的该类的所有对象)以保证并发环境的线程安全。同一时刻只有一个线程可以获得锁。

其底层实现是通过使用对象监视器Monitor,每个对象都有一个监视器,当线程试图获取Synchronized锁定的对象时,就会去请求对象监视器(Monitor.Enter()方法),如果监视器空闲,则请求成功,会获取执行锁定代码的权利;如果监视器已被其他线程持有,线程进入同步队列等待。

2、Lock同步锁:与synchronized功能类似,可从Lock与synchronized区别进行分析

   1、Lock可以通过tryLock()方法非阻塞地获取锁而。如果获取了锁即立刻返回true,否则立刻返回false。这个方法还有加上定       时等待的重载方法tryLock(long time, TimeUnit unit)方法,在定时期间内,如果获取了锁立刻返回true,否则在定时结束后返回         false。在定时等待期间可以被中断,抛出InterruptException异常。而Synchronized在获得锁的过程中是不可被中断的。

   2、Lock可以通过lockInterrupt()方法可中断的获取锁,与lock()方法不同的是等待时可以响应中断,抛出InterruptException异常。

   3、Synchronized是隐式的加锁解锁,而Lock必须显示的加锁解锁,而且解锁应放到finnally中,保证一定会被解锁,而         Synchronized在出现异常时也会自动解锁。但也因为这样,Lock更加灵活。

   4、Synchronized是JVM层面上的设计,对对象加锁,基于对象监视器。Lock是代码实现的。

3、可重入锁ReentrantLock与Synchronized都是可重入锁可重入意味着,获得锁的线程可递归的再次获取锁。当所有锁释放后,其他线程才可以获取锁。

4、公平锁与非公平锁:“公平性”是指是否等待最久的线程就会获得资源。如果获得锁的顺序是顺序的,那么就是公平的。不公平锁一般效率高于公平锁。ReentrantLock可以通过构造函数参数控制锁是否公平。

5、ReentrantReadWriteLock读写锁:是一种非排它锁, 一般的锁都是排他锁,就是同一时刻只有一个线程可以访问,比如Synchronized和Lock。读写锁就多个线程可以同时获取读锁读资源,当有写操作的时候,获取写锁,写操作之后的读写操作都将被阻塞,直到写锁释放。读写锁适合写操作较多的场景,效率较高。

6、乐观锁与悲观锁:在Java中的实际应用类并不多,大多用在数据库锁上

正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制

乐观锁,大多是基于数据版本( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现

7、死锁:是当两个线程互相等待获取对方的对象监视器时就会发生死锁。一旦出现死锁,整个程序既不会出现异常也不会有提示,但所有线程都处于阻塞状态。死锁一般出现于多个同步监视器的情况。

java集合

ArrayList和LinkedList

  • ArrayList:长度可变的数组,可以对元素进行随机的访问,向ArrayList中插入元素与删除元素的速度慢。JDK8 中ArrayList扩容的实现是通过grow()方法里使用语句newCapacity = oldCapacity + (oldCapacity >> 1)(即1.5倍扩容)计算容量,然后调用Arrays.copyof()方法进行对原数组进行复制。
  • LinkedList:采用链表数据结构,插入和删除速度快,但随机访问速度慢。

HashSet和TreeSet

  • HashSet: HashSet按照哈希算法来存取集合中的对象,存取速度比较快。HashSet可实现去重,HashSet使用add()方法添加对象,添加的对象存在键上,这也是Hashset 实现去重的原因,Hashset的值是固定值,
    HashSet会先调用元素的hashCode方法得到元素的哈希值 ,
        然后通过元素 的哈希值经过移位等运算,就可以算出该元素在哈希表中 的存储位置。
        1.如果算出元素存储的位置目前没有任何元素存储,那么该元素可以直接存储到该位置上。
        2.如果算出该元素的存储位置目前已经存在有其他的元素了,那么会调用该元素的equals方法与该位置的元素再比较一次
        ,如果equals返回的是true,那么该元素与这个位置上的元素就视为重复元素,不允许添加,如果equals方法返回的是false,那么该元素运行 添加。
  • TreeSet:TreeSet实现了SortedSet接口,能够对集合中的对象进行排序。

HashMap和LinkedHashMap和TreeMap

HashMap:HashMap是基于hashing的原理,我们使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象

当两个对象的hashcode相同会发生什么

因为hashcode相同,所以它们的bucket位置相同,‘碰撞’会发生。因为HashMap使用链表存储对象,这个Entry(包含有键值对的Map.Entry对象)会存储在链表的下一个节点中。

如果两个键的hashcode相同,你如何获取值对象

当我们调用get()方法,HashMap会使用键对象的hashcode找到bucket位置,找到bucket位置之后,会调用keys.equals()方法去找到链表中正确的节点,最终找到要找的值对象

LinkedHashMap:类似于HashMap,但是迭代遍历它时,取得<K,V>的顺序是其插入次序,或者是最近最少使用(LRU)的次序。

TreeMap:TreeMap基于红黑树实现。查看<K,V>时,它们会被排序。TreeMap是唯一的带有subMap()方法的Map,subMap()可以返回一个子树。

 

==与equals区别

1)对于==

如果作用于基本数据类型的变量,则比较 “值”是否相等;    

如果作用于引用类型的变量,则比较所指向的对象的地址 

2)对于equals方法,

注意:equals方法不能作用于基本数据类型的变量,equals继承Object类,比较的是是否是同一个对象    

如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值