javasync
㈠ 找到卡顿来源,BlockCanary源码精简分析
通过屏幕渲染机制我们了解到,Android的屏幕渲染是通过vsync实现的。软件层将数据计算好后,放入缓冲区,硬件层从缓冲区读取数据绘制到屏幕上,渲染周期是16ms,这让我们看到不断变化的画面。如果计算时间超过16ms,就会出现卡顿现象,这通常发生在软件层,而不是硬件层。卡顿发生的原因在于软件层的计算时间需要小于16ms,而计算的执行地点则在Handler中,具体来说是在UI的Handler中。Android进程间的交互通过Binder实现,线程间通信通过Handler。
软件层在收到硬件层的vsync信号后,会在java层向UI的Handler中投递一个消息,进行view数据的计算。这涉及到测量、布局和绘制,通常在`ViewRootImpl`的`performTraversals()`函数中实现。因此,view数据计算在UI的Handler中执行,如果有其他操作在此执行且耗时过长,则可能导致卡顿,我们需要找到并优化这些操作。
要找到卡顿的原因,可以通过在消息处理前后记录时间,计算时间差,将这个差值与预设的卡顿阈值比较。如果大于阈值,表示发生了卡顿,此时可以mp主线程堆栈并显示给开发者。实现这一功能的关键在于在Looper中设置日志打印类。通过`Looper.loop()`函数中的日志打印,我们可以插入自定义的Printer,并在消息执行前后计算时间差。另一种方法是在日志中添加前缀和后缀,根据这些标志判断时间点。
BlockCanary是一个用于检测Android应用卡顿的工具,通过源码分析,我们可以了解到它的实现逻辑。要使用BlockCanary,首先需要定义一个继承`BlockCanaryContext`的类,并重写其中的关键方法。在应用的`onCreate()`方法中调用BlockCanary的安装方法即可。当卡顿发生时,BlockCanary会通知开发者,并在日志中显示卡顿信息。
BlockCanary的核心逻辑包括安装、事件监控、堆栈和CPU信息的采集等。在事件发生时,会创建LooperMonitor,同时启动堆栈采样和CPU采样。当消息将要执行时,开始记录开始时间,执行完毕后停止记录,并计算执行时间。如果时间差超过预设阈值,表示发生了卡顿,并通过回调传递卡顿信息给开发者。
堆栈和CPU信息的获取通过`AbstractSampler`类实现,它通过`post`一个`Runnable`来触发采样过程,循环调用`doSample()`函数。StackSampler和CpuSampler分别负责堆栈和CPU信息的采集,核心逻辑包括获取当前线程的堆栈信息和CPU速率,并将其保存。获取堆栈信息时,通过在`StackSampler`类中查找指定时间范围内的堆栈信息;获取CPU信息时,从`CpuSampler`类中解析`/proc/stat`和`/proc/mpid/stat`文件的CPU数据,并保存。
总结而言,BlockCanary通过在消息处理前后记录时间差,检测卡顿情况,并通过堆栈和CPU信息提供详细的卡顿分析,帮助开发者定位和优化性能问题。
㈡ java多线程中synchronized关键字的用法
由于同一进程内的多个线程共享内存空间 在Java中 就是共享实例 当多个线程试图同时修改某个实例的内容时 就会造成冲突 因此 线程必须实现共享互斥 使多线程同步
最简单的同步是将一个方法标记为synchronized 对同一个实例来说 任一时刻只能有一个synchronized方法在执行 当一个方法正在执行某个synchronized方法时 其他线程如果想要执行这个实例的任意一个synchronized方法 都必须等待当前执行 synchronized方法的线程退出此方法后 才能依次执行
但是 非synchronized方法不受影响 不管当前有没有执行synchronized方法 非synchronized方法都可以被多个线程同时执行
此外 必须注意 只有同一实例的synchronized方法同一时间只能被一个线程执行 不同实例的synchronized方法是可以并发的 例如 class A定义了synchronized方法sync() 则不同实例a sync()和a sync()可以同时由两个线程来执行
多线程同步的实现最终依赖锁机制 我们可以想象某一共享资源是一间屋子 每个人都是一个线程 当A希望进入房间时 他必须获得门锁 一旦A获得门锁 他进去后就立刻将门锁上 于是B C D就不得不在门外等待 直到A释放锁出来后 B C D中的某一人抢到了该锁(具体抢法依赖于 JVM的实现 可以先到先得 也可以随机挑选) 然后进屋又将门锁上 这样 任一时刻最多有一人在屋内(使用共享资源)
Java语言规范内置了对多线程的支持 对于Java程序来说 每一个对象实例都有一把 锁 一旦某个线程获得了该锁 别的线程如果希望获得该锁 只能等待这个线程释放锁之后 获得锁的方法只有一个 就是synchronized关键字 例如
public class SharedResource {
private int count = ;
public int getCount() { return count; }
public synchronized void setCount(int count) { unt = count; }
}
同步方法public synchronized void setCount(int count) { unt = count; } 事实上相当于
public void setCount(int count) {
synchronized(this) { // 在此获得this锁
unt = count;
} // 在此释放this锁
}
红色部分表示需要同步的代码段 该区域为 危险区域 如果两个以上的线程同时执行 会引发冲突 因此 要更改SharedResource的内部状态 必须先获得SharedResource实例的锁
退出synchronized块时 线程拥有的锁自动释放 于是 别的线程又可以获取该锁了
为了提高性能 不一定要锁定this 例如 SharedResource有两个独立变化的变量
public class SharedResouce {
private int a = ;
private int b = ;
public synchronized void setA(int a) { this a = a; }
public synchronized void setB(int b) { this b = b; }
}
若同步整个方法 则setA()的时候无法setB() setB()时无法setA() 为了提高性能 可以使用不同对象的锁
public class SharedResouce {
private int a = ;
private int b = ;
private Object sync_a = new Object()
private Object sync_b = new Object()
public void setA(int a) {
synchronized(sync_a) {
this a = a;
}
}
public synchronized void setB(int b) {
synchronized(sync_b) {
this b = b;
}
lishixin/Article/program/Java/gj/201311/27512
㈢ 如何解决java 多线程问题
Java线程同步需要我们不断的进行相关知识的学习,下面我们就来看看如何才能更好的在学习中掌握相关的知识讯息,来完善我们自身的编写手段。希望大家有所收获。 Java线程同步的优先级代表该线程的重要程度,当有多个线程同时处于可执行状态并等待获得 CPU 时间时,线程调度系统根据各个线程的优先级来决定给谁分配 CPU 时间,优先级高的线程有更大的机会获得 CPU 时间,优先级低的线程也不是没有机会,只是机会要小一些罢了。 你可以调用 Thread 类的方法 getPriority()和 setPriority()来存取Java线程同步的优先级,线程的优先级界于1(MIN_PRIORITY)和10(MAX_PRIORITY)之间,缺省是5(NORM_PRIORITY)。 Java线程同步 由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。 由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是 synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块。 1. synchronized 方法:通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。如:1. public synchronized void accessVal(int newVal); synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的Java线程同步方能获得该锁,重新进入可执行状态。 这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。 在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。 synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run()声明为 synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,但是 Java 为我们提供了更好的解决办法,那就是 synchronized 块。 2. synchronized 块:通过 synchronized关键字来声明synchronized 块。语法如下:1. synchronized(syncObject)2. {3. //允许访问控制的代码4. } synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。 Java线程同步的阻塞 为了解决对共享存储区的访问冲突,Java 引入了同步机制,现在让我们来考察多个Java线程同步对共享资源的访问,显然同步机制已经不够了,因为在任意时刻所要求的资源不一定已经准备好了被访问,反过来,同一时刻准备好了的资源也可能不止一个。为了解决这种情况下的访问控制问题,Java 引入了对阻塞机制的支持。 阻塞指的是暂停一个Java线程同步的执行以等待某个条件发生(如某资源就绪),学过操作系统的同学对它一定已经很熟悉了。Java 提供了大量方法来支持阻塞,下面让我们逐一分析。