Java层使用pipe完善阻塞式编程
背景
java层while循环,如果是非阻塞的循环,会极度浪费cpu资源,也会导致耗电严重,app被吐槽。
Android系统层一般会使用消息通知机制,比如:
looper机制,无消息时就会阻塞。
linux IO多路复用
linux pipe管道
具体的就不多说了,我们今天的主题是java层使用pipe
java层有很多阻塞类可使用,比如ConditionVariable
通过源代码代码可以看到,block函数其实使用了wait, notify。
作用:ConditionVariable其实是在一个线程加锁,同一个线程调用open解锁,其他线程调用block函数等待锁打开
package android.os;
/**
* Class that implements the condition variable locking paradigm.
*
* <p>
* This differs from the built-in java.lang.Object wait() and notify()
* in that this class contains the condition to wait on itself. That means
* open(), close() and block() are sticky. If open() is called before block(),
* block() will not block, and instead return immediately.
*
* <p>
* This class uses itself is at the object to wait on, so if you wait()
* or notify() on a ConditionVariable, the results are undefined.
*/
public class ConditionVariable
{
private volatile boolean mCondition;
/**
* Create the ConditionVariable in the default closed state.
*/
public ConditionVariable()
{
mCondition = false;
}
/**
* Create the ConditionVariable with the given state.
*
* <p>
* Pass true for opened and false for closed.
*/
public ConditionVariable(boolean state)
{
mCondition = state;
}
public void open()
{
synchronized (this) {
boolean old = mCondition;
mCondition = true;
if (!old) {
this.notifyAll();
}
}
}
/**
* Reset the condition to the closed state.
*
* <p>
* Any threads that call block() will block until someone calls open.
*/
public void close()
{
synchronized (this) {
mCondition = false;
}
}
/**
* Block the current thread until the condition is opened.
*
* <p>
* If the condition is already opened, return immediately.
*/
public void block()
{
synchronized (this) {
while (!mCondition) {
try {
this.wait();
}
catch (InterruptedException e) {
}
}
}
}
/**
* Block the current thread until the condition is opened or until
* timeout milliseconds have passed.
*
* <p>
* If the condition is already opened, return immediately.
*
* @param timeout the minimum time to wait in milliseconds.
*
* @return true if the condition was opened, false if the call returns
* because of the timeout.
*/
public boolean block(long timeout)
{
// Object.wait(0) means wait forever, to mimic this, we just
// call the other block() method in that case. It simplifies
// this code for the common case.
if (timeout != 0) {
synchronized (this) {
long now = System.currentTimeMillis();
long end = now + timeout;
while (!mCondition && now < end) {
try {
this.wait(end-now);
}
catch (InterruptedException e) {
}
now = System.currentTimeMillis();
}
return mCondition;
}
} else {
this.block();
return true;
}
}
}
今天,我们使用一种更原生的方式,java也支持管道通讯,可实现阻塞编程
代码地址: https://github.com/whulzz1993/AndroidDemo/tree/master
如何创建管道?
通过android.os.ParcelFileDescriptor.createPipe返回fd[2],然后使用fd[0]作为read, fd[1]作为write
package com.example.javapipe;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class JavaPipe {
private static final String TAG = "JavaPipe";
InputStream mPipeRead = null;
OutputStream mPipeWrite = null;
public JavaPipe() {
initPipe();
}
private void initPipe() {
try {
ParcelFileDescriptor[] pipes = ParcelFileDescriptor.createPipe();
mPipeRead = new ParcelFileDescriptor.AutoCloseInputStream(pipes[0]);
mPipeWrite = new ParcelFileDescriptor.AutoCloseOutputStream(pipes[1]);
} catch (IOException e) {
Log.e(TAG, "initPipe failed!", e);
mPipeRead = null;
mPipeWrite = null;
}
}
public void pipeRead() {
if (mPipeRead != null) {
int tryCount = 3;
do {
try {
mPipeRead.read();
break;
} catch (IOException e) {
e.printStackTrace();
continue;
}
} while (--tryCount > 0);
}
}
public void pipeWrite() {
if (mPipeWrite != null) {
int tryCount = 3;
do {
try {
mPipeWrite.write(1);
break;
} catch (IOException e) {
e.printStackTrace();
continue;
}
} while (--tryCount > 0);
}
}
}
如何在代码中使用
可以直接new Javapipe,然后在while循环中读取管道消息进行阻塞
private void testPipe() {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
long start = System.currentTimeMillis();
mPipe.pipeRead();
Log.d(TAG, String.format("pipeRead success cost %dms",
System.currentTimeMillis() - start));
}
}
}).start();
}
如果不往管道中写,则会一直阻塞下去
我测试的时候是通过点击按钮触发通道写入的
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
//通道写消息
mPipe.pipeWrite();
}
});
mPipe = new JavaPipe();
testPipe();
}
看一下点击按钮的log:
2021-03-19 20:00:21.462 31471-31517/com.example.demo D/MainActivity: pipeRead success cost 13691ms
2021-03-19 20:00:27.621 31471-31517/com.example.demo D/MainActivity: pipeRead success cost 6159ms
2021-03-19 20:00:39.938 31471-31517/com.example.demo D/MainActivity: pipeRead success cost 12317ms
2021-03-19 20:22:28.041 31471-31517/com.example.demo D/MainActivity: pipeRead success cost 1308101ms