- 问:简述线程和进程的区别?
进程
1、操作系统进行资源分配和调度的基本单位,多个进程之间相互独立;
2、稳定性好,如果一个进程崩溃,不影响其他进程,但是进程消耗资源大,开启的进程数量有限制;
线程
1、CPU进行资源分配和调度的基本单位,线程是进程的一部分,是比进程更小的能独立运行的基本单位,一个进程下的多个线程可以共享该进程的所有资源;
2、如果IO操作密集,则可以多线程运行效率高,缺点是如果一个线程崩溃,都会造成进程的崩溃;
3、线程自己不拥有系统资源;
4、一个线程可以创建和撤销另一个线程;
- 问:一个程序至少有一个进程,一个进程至少有一个线程?
- 答:错误,一个程序至少有一个进程,不对。程序放在硬盘里没有执行,不会有对应的进程。程序(program)只能有一个进程,一个进程就是一个程序。有人说,我打开一个程序,比如chrome,有十多个进程呢,这是咋回事。那就是十多个程序,操作系统给他们分配了彼此独立的内存,相互执行不受彼此约束,分配同样时间的CPU。对于用户而言,他们是一个整体,我们通常称之为应用程序(application)。对于计算机而言,一个进程就是一个程序,多个进程(比如一个浏览器的多个进程)对计算机而言就是多个不同的程序,它不会把它们理解为一个完整的“程序”。
问:进程调度方式?
问:多线程优点?
- 问:内存泄露 和 内存溢出 的联系与区别?
一、内存溢出(memory overflow)
指程序在申请内存时,没有足够的内存供申请者使用,或者说,给了你一块存储 int 类型数据的存储空间,但是你却存储 long 类型的数据,就会导致内存不够用,报错 OOM,即出现内存溢出的错误。
二、内存泄漏(memory leak)
1、内存泄漏是指程序中已动态分配的堆内存由于某种原因未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统奔溃等严重后果;
2、一次内训泄漏似乎不会有大的影响,但内存泄漏后堆积的结果就是内存溢出;
3、内存泄漏具有隐蔽性,积累性的特征,比其他内存非法访问错误更难检测。这是因为内存泄漏产生的原因是内存块未被释放,属于遗漏型缺陷而不是过错型缺陷。此外,内存泄漏不会直接产生可观察的错误,而是逐渐积累,降低系统的整体性性能;
4、如何有效的进行内存分配和释放,防止内存泄漏,是软件开发人员的关键问题,比如一个服务器应用软件要长时间服务多个客户端,若存在内存泄漏,则会逐渐堆积,导致一系列严重后果;
- 问:写个LRU算法?
- 答:LRU(Least Recently Used),即最近最少使用算法:
设计并实现了一个最近最少使用(LRU)缓存的数据结构,它应该支持以下操作 get 和 set:
get(key)——如果键存在于缓存中,则获得键值(总是正数),否则返回-1;
set(key, value)——如果键不存在,则设置或插入值。当缓存达到其容量时,应在插入新项之前使最近最少使用的项无效;
分析:LRU 缓存可使用一个 HashMap 和双向链表实现。HashMap,使得get的时间是O(1);双向链表使节点添加/删除操作O(1)。
/**
* LRU Cache
* LRU缓存机制
*/
public class Solution2 {
public class LRUCache {
private class Node {
int key;
int value;
LRUCache.Node prev;
LRUCache.Node next;
Node(int k, int v) {
this.key = k;
this.value = v;
}
Node() {
this.key = 0;
this.value = 0;
}
}
private int capacity;
private int count;
private LRUCache.Node head;
private LRUCache.Node tail;
private Map<Integer, LRUCache.Node> map;
// ATTN: the value should be Node type! This is the whole point of having a class called Node!
public LRUCache(int capacity) {
this.capacity = capacity;
this.count = 0;// we need a count to keep track of the number of elements in the cache so
// that we know when to evict the LRU one from the cache
this.map = new HashMap();
head = new LRUCache.Node();
tail = new LRUCache.Node();
head.next = tail;
tail.prev = head;
}
public int get(int key) {
LRUCache.Node node = map.get(key);
// HashMap allows value to be null, this is superior than HashTable!
if (node == null) {
return -1;
} else {
remove(node);
add(node);
return node.value;
}
}
public void set(int key, int value) {
LRUCache.Node node = map.get(key);
if (node == null) {
node = new LRUCache.Node(key, value);
map.put(key, node);
add(node);
count++;
if (count > capacity) {
LRUCache.Node toDelete = tail.prev;
map.remove(toDelete.key);
remove(toDelete);
count--;
}
} else {
remove(node);
node.value = value;
add(node);
}
}
private void remove(LRUCache.Node node) {
LRUCache.Node next = node.next;
LRUCache.Node prev = node.prev;
prev.next = next;
next.prev = prev;
}
private void add(LRUCache.Node node) {
// ATTN: we'll always add the node into the first position: head.next!!!!
LRUCache.Node next = head.next;
head.next = node;
node.next = next;
node.prev = head;
next.prev = node;
}
}
}