Java基础宝典(二)

作者: 简单就好_1897 | 来源:发表于2019-04-23 10:53 被阅读0次

11.ThreadLocal

    很多地方叫做线程本地变量,或者线程本地存储。

    首先,在每个线程内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadlocals,这个threadlocals就是用来存储实际的变量副本,键值为当前ThreadLocal变量,

    value为变量的副本(即T类型的变量)。

    ThreadLocal中的get()代码

      public T get() {

        Thread t = Thread.currentThread();

        ThreadLocalMap map = getMap(t);

        if (map != null) {

            ThreadLocalMap.Entry e = map.getEntry(this);

            if (e != null) {

                @SuppressWarnings("unchecked")

                T result = (T)e.value;

                return result;

            }

        }

        return setInitialValue();

    }

  ThreadLocalMap getMap(Thread t) {

  return t.threadLocals;

}

    private static ThreadLocal<Object> threadLocal = new ThreadLocal<Object>() {

@Override

protected Object initialValue() {

System.out.println("调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!");

return null;

}

};

      总结一下:

  1)实际的通过ThreadLocal创建的副本是存储在每个线程自己的threadLocals中的;

  2)为何threadLocals的类型ThreadLocalMap的键值为ThreadLocal对象,因为每个线程中可有多个threadLocal变量,就像上面代码中的longLocal和stringLocal;

  3)在进行get之前,必须先set,否则会报空指针异常;

      如果想在get之前不需要调用set就能正常访问的话,必须重写initialValue()方法。

     因为在上面的代码分析过程中,我们发现如果没有先set的话,即在map中查找不到对应的存储,则会通过调用setInitialValue方法返回i,

        而在setInitialValue方法中,有一个语句是T value = initialValue(), 而默认情况下,initialValue方法返回的是null。

      ThreadLocal进行set的时候,是在当前线程Thread中获取到有且唯一的ThreadLocalMap对象(如果没有就新建一个ThreadLocalMap对象设置进Thread的属性里),然后把自己作为键,value作为值set进这个Map里

      ThreadLocal进行get的时候,是从当前线程Thread中获取到有且唯一的ThreadLocalMap对象(Thread的ThreadLocalMap属性如果为空,也就是说这个线程从来都没有用过ThreadLocal设置过值,返回null),然后把自己做为键去该Map里面找,找到就返回对于的value,没有就返回null

  ThreadLocal的应用场景

    最常见的ThreadLocal使用场景为 用来解决 数据库连接、Session管理等。

12. wait()和sleep()的区别

  1.wait()是Object类中的方法,而sleep()是Thread中的方法

      2.wait会释放锁,而sleep无法释放锁。

            sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源,其他线程可以占用CPU

      3. 使用范围:

            1)wait,notify,notifyAll方法只能在同步方法或者同步代码块中

            2)sleep可以在任何地方使用。

      4.使用wait,notify,notifyAll 不用捕获异常,而sleep方法必须捕获异常

13.Intent传递数据最大多少呢,他又是如何传递到另外一个Activity呢?

    1.最大携带的Bundle数据为1M

    2.简单来说,系统进程中的AMS集中负责管理所有进程中的Activity。app进程与系统进程需要进行双向通信。

        比如打开一个新的Activity,则需要调用系统进程AMS中的方法进行实现,AMS等实现完毕需要回调app进程中的相关方法进行具体activity生命周期的回调。

      所以我们在intent中携带的数据也要从APP进程传输到AMS进程,再由AMS进程传输到目标Activity所在进程。有同学可能由疑问了,目标Acitivity所在进程不就是APP进程吗?

      其实不是的,我们可以在Manifest.xml中设置android:process属性来为Activity, Service等指定单独的进程,所以Activity的startActivity方法是原生支持跨进程通信的。

  startActivity携带的数据会经过BInder内核再传递到目标Activity中去,因为binder映射内存的限制(比1MB略小的值),所以startActivity也就会这个限制了。

14.ANR问题

  1.Activity 5秒

  2.Broadcast 10秒

  3.Service20秒

1.耗时的网络访问

2.大量的数据读写

3.数据库操作

4.硬件操作(比如camera)

5.调用thread的join()方法、sleep()方法、wait()方法或者等待线程锁的时候

6.service binder的数量达到上限

7.system server中发生WatchDog ANR

8.service忙导致超时无响应

9.其他线程持有锁,导致主线程等待超时

10.其它线程终止或崩溃导致主线程一直等待。

11.lowmemorykill

PS

1:UI线程尽量只做跟UI相关的工作

2:CPU满负荷, I/O阻塞的 耗时的工作(比如数据库操作,I/O,连接网络或者别的有可能阻碍UI线程的操作)把它放入单独的线程处理

3:尽量用Handler来处理UIthread和别的thread之间的交互

4. 内存不够用的,增大VM内存, 使用largeHeap属性, 排查内存泄露.

---------------------------------------------------------------------------------------------------

Activity的所有生命周期回调都是执行在主线程的.

Service默认是执行在主线程的.

BroadcastReceiver的onReceive回调是执行在主线程的.

没有使用子线程的looper的Handler的handleMessage, post(Runnable)是执行在主线程的.

AsyncTask的回调中除了doInBackground, 其他都是执行在主线程的.

View的post(Runnable)是执行在主线程的.

IntentService 是 Service 的子类,并且所有的请求操作都是在异步线程里。如果不需要 Service 来同时处理多个请求的话,IntentService 将会是最佳的选择。

使用该服务只需要继承并重写 IntentService 中的 onHandleIntent() 方法,就可以对接受到的 Intent 做后台的异步线程操作了。

服务保活

1.提升 Service 的优先级  优先级数值最高为 1000,数字越小,优先级越低。

2.在 Manifest.xml 文件中设置 persistent 属性为 true,则可使该服务免受 out-of-memory killer 的影响。

3.将服务改成前台服务  重写 onStartCommand 方法,使用 startForeground(int, Notification) 方法来启动 Service。

15.序列化

序列化是一种用来处理对象流的机制。

所谓对象流:就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。

序列化是为了解决在对对象流进行读写操作时所引发的问题。

序列化的实现:将需要被序列化的类实现Serializable接口(标记接口),该接口没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的,

然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象;

接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。

1.概念

序列化:把Java对象转换为字节序列的过程。

反序列化:把字节序列恢复为Java对象的过程。

2.用途 对象的序列化主要有两种用途:

1)把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中。

2)在网络上传送对象的字节序列。

16. IPC

进程间通信(IPC,Inter-Process Communication),指至少两个进程或线程间传送数据或信号的一些技术或方法。

进程是计算机系统分配资源的最小单位(严格说来是线程)。每个进程都有自己的一部分独立的系统资源,彼此是隔离的。

使用IPC 的理由:

信息共享:Web服务器,通过网页浏览器使用进程间通信来共享web文件(网页等)和多媒体。

加速:维基百科使用通过进程间通信进行交流的多服务器来满足用户的请求。

模块化。

私有权分离。

与直接共享内存地址空间的多线程编程相比,IPC的缺点:

采用了某种形式的内核开销,降低了性能;

几乎大部分IPC都不是程序设计的自然扩展,往往会大大地增加程序的复杂度。

我们知道Android中要实现IPC,有好多种方式:

1)在Intent中附加extras来传递信息。

2)共享文件。

3)SharedPreferences(不建议在进程间通信中使用,因为在多进程模式下,系统对SharedPreferences的读/写会变得不可靠,面对高并发的读/写访问,有很大几率会丢失数据)。

4)基于Binder的AIDL。

5)基于Binder的Messenger(翻译为“信使”,其实Messenger本质上也是AIDL,只不过系统做了封装以方便上层调用)。

6)Socket。

7)天生支持跨进程访问的ContentProvider。

IPC的跨进程通讯示意图如下:

Binder通讯内存映射的示意图如下:

17 .SharePreferences

SharePreferences是Android基于xml实现的一种数据持久话手段

SharePreferences不支持多进程

SharePreferences的commit与apply一个是同步一个是异步(大部分场景下)

不要使用SharePreferences存储太大的数据

18 大图片加载以及处理。

https://blog.csdn.net/u012426327/article/details/78088104

相关文章

网友评论

    本文标题:Java基础宝典(二)

    本文链接:https://www.haomeiwen.com/subject/epwmgqtx.html