// ReentrantLock 可重入锁,
// 一个线程可以对已被加锁的ReentrantLock锁再次加锁
private Lock lock = new ReentrantLock();
public static void main(String[] args) {
Test tt = new Test();
new Thread(tt.new Adder()).start();
new Thread(tt.new SubTractor()).start();
}
private class SubTractor implements Runnable {
@Override
public void run() {
while (true) {
// synchronized (Test.this) {
// System.out.println(“j–=” + j–);
// //这里抛异常了,锁能释放吗?
// }
lock.lock();
try {
System.out.println("j-- = " + j–);
} finally {
lock.unlock();
}
}
}
}
private class Adder implements Runnable {
@Override
public void run() {
while (true) {
// synchronized (Test.this) {
// System.out.println(“j++=” + j++);
// }
lock.lock();
try {
System.out.println("j++ = " + j++);
} finally {
lock.unlock();
}
}
}
}
}
执行结果:
···
j++ = 42218
j++ = 42219
j++ = 42220
j-- = 42221
j-- = 42220
j-- = 42219
···
8. 设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。写出程序
public class Test {
private int j;
public static void main(String args[]) {
Test tt = new Test();
Inc inc = tt.new Inc();
Dec dec = tt.new Dec();
for (int i = 0; i < 2; i++) {
Thread t = new Thread(inc);
t.start();
t = new Thread(dec);
t.start();
}
}
private synchronized void inc() {
j++;
System.out.println(Thread.currentThread().getName() + “-inc:” + j);
}
private synchronized void dec() {
j–;
System.out.println(Thread.currentThread().getName() + “-dec:” + j);
}
class Inc implements Runnable {
public void run() {
for (int i = 0; i < 100; i++) {
inc();
}
}
}
class Dec implements Runnable {
public void run() {
for (int i = 0; i < 100; i++) {
dec();
}
}
}
}
执行结果:
···
Thread-0-inc:5
Thread-0-inc:6
Thread-2-inc:7
Thread-2-inc:8
···
Thread-2-inc:105
Thread-2-inc:106
Thread-3-dec:105
Thread-3-dec:104
···
Thread-3-dec:7
Thread-3-dec:6
Thread-1-dec:5
Thread-1-dec:4
···
Thread-1-dec:-20
Thread-1-dec:-21
Thread-0-inc:-20
Thread-0-inc:-19
···
9. 子线程循环10次,接着主线程循环5次,接着又回到子线程循环10次,接着再回到主线程又循环5次数。如此循环50次,请写出程序
public class Test {
public static void main(String[] args) {
new Test().init();
}
public void init() {
final Business business = new Business();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 50; i++) {
business.SubThread(i); // 子线程
}
}
}).start();
for (int i = 0; i < 50; i++) {
business.MainThread(i); // 主线程
}
}
private class Business {
//这里相当于定义了控制该谁执行的一个信号灯
boolean isShouldSub = true;
public synchronized void MainThread(int i) {
if (isShouldSub)
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int j = 0; j < 5; j++) {
System.out.println(Thread.currentThread().getName() + “:i=” + i + “,j=” + j);
}
isShouldSub = true;
this.notify();
}
public synchronized void SubThread(int i) {
if (!isShouldSub) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 0; j < 10; j++) {
System.out.println(Thread.currentThread().getName() + “:i=” + i + “,j=” + j);
}
isShouldSub = false;
this.notify();
}
}
}
执行结果:
···
Thread-0:i=48,j=7
Thread-0:i=48,j=8
Thread-0:i=48,j=9
main:i=48,j=0
main:i=48,j=1
main:i=48,j=2
main:i=48,j=3
main:i=48,j=4
Thread-0:i=49,j=0
Thread-0:i=49,j=1
Thread-0:i=49,j=2
Thread-0:i=49,j=3
Thread-0:i=49,j=4
Thread-0:i=49,j=5
Thread-0:i=49,j=6
Thread-0:i=49,j=7
Thread-0:i=49,j=8
Thread-0:i=49,j=9
main:i=49,j=0
main:i=49,j=1
main:i=49,j=2
main:i=49,j=3
main:i=49,j=4
10. ArrayList和Vector的区别
- 同步性:
Vector是线程安全的,也就是说是它的方法之间是线程同步的。而ArrayList是线程序不安全的,它的方法之间是线程不同步的。如果只有一个线程会访问到集合,那最好是使用ArrayList,因为它不考虑线程安全,效率会高些;如果有多个线程会访问到集合,那最好是使用Vector,因为不需要我们自己再去考虑和编写线程安全的代码。
- 数据增长:
ArrayList与Vector都有一个初始的容量大小,当存储进它们里面的元素的个数超过了容量时,就需要增加ArrayList与Vector的存储空间,每次要增加存储空间时,不是只增加一个存储单元,而是增加多个存储单元,每次增加的存储单元的个数在内存空间利用与程序效率之间要取得一定的平衡。Vector默认增长为原来两倍,而ArrayList的增长策略在文档中没有明确规定(从源代码看到的是增长为原来的1.5倍)。ArrayList与Vector都可以设置初始的空间大小,Vector还可以设置增长的空间大小,而ArrayList没有提供设置增长空间的方法。
备注:
对于Vector
&ArrayList
、Hashtable
&HashMap
,要记住线程安全的问题,记住Vector与Hashtable是旧的,是java一诞生就提供了的,它们是线程安全的,ArrayList与HashMap是java2时才提供的,它们是线程不安全的。
补充:
这两个类 都实现了List接口(List接口继承了Collection接口),他们都是有序集合,即存储在这两个集合中的元素的位置都是有顺序的,相当于一种动态的数组,我们以后可以按位置索引号取出某个元素,,并且其中的数据是允许重复的,这是HashSet之类的集合的最大不同处,HashSet之类的集合不可以按索引号去检索其中的元素,也不允许有重复的元素
11. Collection 和 Collections的区别?
Collection
是集合类的上级接口,继承与他的接口主要有Set 和List.
Collections
是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。
12. 字节流与字符流的区别?
java中主要有两种流:
1. 字节流,继承于InputStream
OutputStream
。
2. 字符流,继承于InputStreamReader
OutputStreamWriter
。
字符流是字节流的包装。
字符向字节转换时,要注意编码的问题,因为字符串转成字节数组,其实是转成该字符的某种编码的字节形式,读取也是反之的道理。
13. 什么是java序列化,如何实现java序列化?或者请解释Serializable接口的作用
我们有时候将一个java对象变成字节流的形式传出去或者从一个字节流中恢复成一个java对象。
例如,要将java对象存储到硬盘或者传送给网络上的其他计算机,这个过程我们可以自己写代码去把一个java对象变成某个格式的字节流再传输。
但是,jre本身就提供了这种支持,我们可以调用OutputStream
的writeObject方法
来做,如果要让java帮我们做,要被传输的对象必须实现serializable接口,这样,javac
编译时就会进行特殊处理,编译的类才可以被writeObject方法
操作,这就是所谓的序列化。需要被序列化的类必须实现Serializable接口,该接口是一个mini接口,其中没有需要实现的方法,implements Serializable
只是为了标注该对象是可被序列化的。
例如,在web开发中,如果对象被保存在了Session中,tomcat在重启时要把Session对象序列化到硬盘,这个对象就必须实现Serializable接口。如果对象要经过分布式系统进行网络传输或通过rmi等远程调用,这就需要在网络上传输对象,被传输的对象就必须实现Serializable接口。
14. java中会存在内存泄漏吗,请简单描述
内存泄露是指一个不再被程序使用的对象或变量一直被占据在内存中。java中有垃圾回收机制,它可以保证一对象不再被引用的时候,即对象编程了孤儿的时候,对象将自动被垃圾回收器从内存中清除掉。
java中的内存泄露的情况:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收。
例如:
-
缓存系统,我们加载了一个对象放在缓存中(例如放在一个全局map对象中),然后一直不再使用它,这个对象一直被缓存引用,但却不再被使用。检查java中的内存泄露,一定要让程序将各种分支情况都完整执行到程序结束,然后看某个对象是否被使用过,如果没有,则才能判定这个对象属于内存泄露。
-
如果一个外部类的实例对象的方法返回了一个内部类的实例对象,这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持久外部类的实例对象,这个外部类对象将不会被垃圾回收,这也会造成内存泄露。
15. volatile关键字是否能保证线程安全?()
答案:不能
解析:volatile关键字用在多线程同步中,可保证读取的可见性,JVM只是保证从主内存加载到线程工作内存的值是最新的读取值,而非cache中。但多个线程对volatile的写操作,无法保证线程安全。
例如:假如线程1,线程2
在进行read,load 操作中,发现主内存中count的值都是5,那么都会加载这个最新的值,在线程1
对count进行修改之后,会write到主内存中,主内存中的count变量就会变为6;线程2
由于已经进行read,load操作,在进行运算之后,也会更新主内存count的变量值为6;导致两个线程及时用volatile关键字修改之后,还是会存在并发的情况。。
16. Java接口的修饰符可以为()
A private B protected C final D abstract
答案:CD
解析:接口很重要,为了说明情况,这里稍微啰嗦点:
- 接口用于描述系统对外提供的所有服务,因此
接口中的成员常量和方法都必须是公开(public)类型
的,确保外部使用者能访问它们;
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
学习福利
【Android 详细知识点思维脑图(技能树)】
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。
这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
由于篇幅有限,这里以图片的形式给大家展示一小部分。
详细整理在GitHub可以见;
TYM1Wm-1710664722835)]
学习福利
【Android 详细知识点思维脑图(技能树)】
[外链图片转存中…(img-mbdx8a6K-1710664722836)]
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。
这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
由于篇幅有限,这里以图片的形式给大家展示一小部分。
[外链图片转存中…(img-djXcS86T-1710664722836)]
详细整理在GitHub可以见;
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。