在下面的示例中,我有两个线程使用的一个文件(在实际示例中,我可以有任意数量的线程)
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class A {
static volatile boolean running = true;
public static void main(String[] args) throws IOException, InterruptedException {
String name = "delete.me";
new File(name).deleteOnExit();
RandomAccessFile raf = new RandomAccessFile(name, "rw");
FileChannel fc = raf.getChannel();
Thread monitor = new Thread(() -> {
try {
while (running) {
System.out.println(name + " is " + (fc.size() >> 10) + " KB");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Interrupted");
Thread.currentThread().interrupt(); …Run Code Online (Sandbox Code Playgroud) 我正在研究使用ByteBuffers从内存映射文件(通过FileChannel.map())以及内存中直接ByteBuffers 构建的东西.我试图了解并发和内存模型约束.
我已经阅读了FileChannel,ByteBuffer,MappedByteBuffer等所有相关的Javadoc(和源代码).很明显,特定的ByteBuffer(和相关的子类)有一堆字段,并且状态不受内存模型的保护观点看法.因此,如果跨线程使用该缓冲区,则必须在修改特定ByteBuffer的状态时进行同步.常见的技巧包括使用ThreadLocal包装ByteBuffer,复制(同步)以获取指向相同映射字节的新实例等.
鉴于这种情况:
B_all为整个文件提供了一个映射的字节缓冲区(比如它<2gb)B_1,该文件的一大块并将其提供给线程T1B_2指向相同映射字节的ByteBuffer 并将其提供给线程T2我的问题是:T1可以写入B_1,T2可以同时写入B_2并保证看到彼此的变化吗?T3可以使用B_all来读取这些字节并保证看到T1和T2的变化吗?
我知道,除非您使用force()指示操作系统将页面写入磁盘,否则不一定会在进程中看到映射文件中的写入.我不在乎.假设这个问题,这个JVM是编写单个映射文件的唯一进程.
注意: 我不是在寻找猜测(我可以自己做得很好).我想引用一些关于内存映射直接缓冲区保证(或不保证)的内容.或者,如果您有实际经验或负面测试用例,那么这也可以作为充分的证据.
更新:我已经完成了一些测试,让多个线程并行写入同一个文件,到目前为止,似乎这些写入可以立即从其他线程中看到.我不确定我是否可以依靠它.
我正在创建一个RandomAccessFile对象,以便通过多个线程写入文件(在SSD上).每个线程都尝试在文件中的特定位置写一个直接字节缓冲区,并确保线程写入的位置不会与另一个线程重叠:
file_.getChannel().write(buffer, position);
Run Code Online (Sandbox Code Playgroud)
where file_的实例是RandomAccessFile和buffer直接字节缓冲区.
对于RandomAccessFile对象,由于我没有使用fallocate来分配文件,并且文件的长度在变化,这是否会利用底层媒体的并发性?
如果不是,在创建文件时如果没有调用fallocate,使用上述函数是否有任何意义?
我正在玩NIO库.我正在尝试在端口8888上侦听连接,一旦接受连接,就从该通道转储所有内容somefile.
我知道该怎么做ByteBuffers,但我想让它与所谓的超高效工作FileChannel.transferFrom.
这就是我得到的:
ServerSocketChannel ssChannel = ServerSocketChannel.open();
ssChannel.socket().bind(new InetSocketAddress(8888));
SocketChannel sChannel = ssChannel.accept();
FileChannel out = new FileOutputStream("somefile").getChannel();
while (... sChannel has not reached the end of the stream ...) <-- what to put here?
out.transferFrom(sChannel, out.position(), BUF_SIZE);
out.close();
Run Code Online (Sandbox Code Playgroud)
所以,我的问题是:如何表达" transferFrom某个频道,直到到达流末端"?
编辑:将1024更改为BUF_SIZE,因为所使用的缓冲区大小与问题无关.
(这与(或者更确切地说是"相反")有关.如果有足够的数据,FileChannel.read会读取比指定的更少的字节吗?)
TL; DR:
这总是会写出整个缓冲区......
ByteBuffer bytes = ...;
fileOutputStream.getChannel().write(bytes);
Run Code Online (Sandbox Code Playgroud)
...或者是否有必要使用这样的循环:
ByteBuffer bytes = ...;
while (bytes.remaining() > 0)
{
fileOutputStream.getChannel().write(bytes);
}
Run Code Online (Sandbox Code Playgroud)
?
由于在另一个答案的评论,我想问一下是否有相关的写的行为的任何担保Buffer的FileChannel调用FileChannel#write(ByteBuffer).
仅供参考:文档说明
从给定缓冲区向该通道写入一个字节序列.
除非通道处于附加模式,否则从该通道的当前文件位置开始写入字节,在这种情况下,位置首先前进到文件的末尾.如果需要,文件会增长以容纳写入的字节,然后使用实际写入的字节数更新文件位置.否则,此方法的行为与WritableByteChannel接口指定的完全相同.
以及被覆盖方法的文档WritableByteChannel#write(ByteBuffer)说
从给定缓冲区向该通道写入一个字节序列.
尝试向通道写入最多r个字节,其中r是缓冲区中剩余的字节数,即src.remaining(),此时调用此方法.
假设写入长度为n的字节序列,其中0 <= n <= r.该字节序列将从索引p开始从缓冲区传输,其中p是调用此方法时缓冲区的位置; 写入的最后一个字节的索引将是p + n - 1.返回时,缓冲区的位置将等于p + n; 它的限制不会改变.
除非另有说明,否则只有在写入所有r请求的字节后才会返回写操作.某些类型的通道(取决于它们的状态)可能只写入一些字节或者根本不写.例如,处于非阻塞模式的套接字通道不能再写入套接字输出缓冲区中可用的字节数.
可以随时调用此方法.但是,如果另一个线程已经在此通道上启动了写操作,则此方法的调用将阻塞,直到第一个操作完成.
参数:src - 要从中检索字节的缓冲区
返回:写入的字节数,可能为零
在上面提到的关于阅读 a的FileChannel问题中,评论中有一些关于本文档的确切措辞和解释的讨论.我认为文档中的关键区别在于对于read方法,文档说
读操作可能不会填充缓冲区,实际上它根本不会读取任何字节.
与此相反,write方法的文档说
除非另有说明,否则只有在写入所有r请求的字节后才会返回写操作.某些类型的通道(取决于它们的状态)可能只写入一些字节或者根本不写.
对我来说,这意味着在一个写操作FileChannel 将所有字节都被写入后只能返回,因为没有在文档中另有规定它(除语句返回值可能为0,但是这显然是从一个神器被覆盖的方法)
从我的文件大小高达80 MB(!)的测试中,写操作总是立即 …
我们希望让用户从任何应用中选择视频,然后将视频剪裁为最多5秒.
为了让Uri被选中,我们让它工作正常(这里有解决方案).
至于修剪本身,我们找不到任何具有许可许可的好图书馆,除了一个名为"k4l-video-trimmer"的图书馆.例如,库"FFmpeg"被认为是非许可,因为它使用GPLv3,这要求使用它的应用程序也是开源的.此外,正如我所读,它需要相当多(约9MB).
可悲的是,这个库(k4l-video-trimmer)很老了,多年没有更新了,所以我不得不把它(这里)分叉,以便很好地处理它.它使用一个名为"mp4parser"的开源库来进行修剪.
问题是,这个库似乎只能处理文件,而不是一个Uri或者InputStream,所以即使样本在选择像普通文件一样无法访问的项目时也会崩溃,甚至还有无法处理的路径.我知道在很多情况下可以获得文件的路径,但在许多其他情况下,它不是,我也知道可以只复制文件(这里),但这不是一个好的解决方案,因为文件可能很大并占用大量空间,即使它已经可以访问.
库有两个地方使用文件:
在"K4LVideoTrimmer"文件中,在"setVideoURI"函数中,它只是获取要显示的文件大小.根据Google的文档,这里的解决方案非常简单:
public void setVideoURI(final Uri videoURI) {
mSrc = videoURI;
if (mOriginSizeFile == 0) {
final Cursor cursor = getContext().getContentResolver().query(videoURI, null, null, null, null);
if (cursor != null) {
int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
cursor.moveToFirst();
mOriginSizeFile = cursor.getLong(sizeIndex);
cursor.close();
mTextSize.setText(Formatter.formatShortFileSize(getContext(), mOriginSizeFile));
}
}
...
Run Code Online (Sandbox Code Playgroud)在"TrimVideoUtils"文件中,在"startTrim"中调用"genVideoUsingMp4Parser"函数.在那里,它使用以下方法调用"mp4parser"库:
Movie movie = MovieCreator.build(new FileDataSourceViaHeapImpl(src.getAbsolutePath()));
Run Code Online (Sandbox Code Playgroud)
它说他们使用FileDataSourceViaHeapImpl(来自"mp4parser"库)来避免Android上的OOM,所以我决定继续使用它.
事实是,它有4个CTORS,都期望文件有一些变化:File,filePath,FileChannel,FileChannel + fileName. …
android filechannel inputstream android-contentresolver mp4parser
我今天遇到了一个我们的实用工具类的问题.它是文件的助手,包含一些静态文件复制例程.以下是与测试方法一起提取的相关方法.
问题是有时setLastModified调用失败,返回false.
在我的PC上(Windows 7,最新的Java),我有时会收到"setLastModified failed"消息(大约25次中的1000次).
我现在通过删除FileChannel.close调用解决了这个问题,但我更愿意理解为什么会发生这种情况,即使这是正确的解决方案.
有没有其他人得到同样的问题?
private void testCopy() throws FileNotFoundException, IOException {
File src = new File("C:\\Public\\Test-Src.txt");
File dst = new File("C:\\Public\\Test-Dst.txt");
for (int i = 0; i < 1000; i++) {
copyFile(src, dst);
}
}
public static void copyFile(final File from, final File to) throws FileNotFoundException, IOException {
final String tmpName = to.getAbsolutePath() + ".tmp";
// Copy to a .tmp file.
final File tmp = new File(tmpName);
// Do the transfer.
transfer(from, tmp);
// Preserve time.
if …Run Code Online (Sandbox Code Playgroud) 我可以将任何InputStream写入FileChannel吗?
我正在使用java.nio.channels.FileChannel打开文件并将其锁定,然后将InputStream写入输出文件.InputStream可以由另一个文件,URL,套接字或任何东西打开.我写了以下代码:
FileOutputStream outputStream = new FileOutputStream(outputFile);
FileChannel outputChannel = outputStream.getChannel();
FileLock lock = outputChannel.lock();
try {
outputChannel.transferFrom(???);
} finally {
lock.release();
outputChannel.close();
outputStream.close();
}
Run Code Online (Sandbox Code Playgroud)
但是,outputChannel.transferFrom(...)的第一个参数请求ReadableByteChannel对象.由于我使用InputStream作为输入,因此它没有inputStream.getChannel()方法来创建所需的通道.
有没有办法从InputStream获取ReadableByteChannel?
我不知道在哪里寻求有关Java API文档和Java代码的澄清和确认,所以我在这里做.
在API文档中FileChannel,我发现在一个以上的地方提交文件position和文件的一个一个错误size.
这只是一个例子.transferFrom(...)州的API文件:
"如果给定位置大于文件的当前大小,则不传输任何字节."
我确认OpenJDK代码也包含此代码...
public long transferFrom(ReadableByteChannel src, long position, long count)
throws IOException
{
// ...
if (position > size())
return 0;
// ...
}
Run Code Online (Sandbox Code Playgroud)
...在FileChannelImpl.java与文档一致的文件中.
现在,虽然上面的代码片段和API文档看起来相互一致,但我觉得"上面应该'大于或等于'而不仅仅是'大于',因为position它是一个基于0的文件数据索引,阅读时position == size()将没有数据返回给来电者!(position == size() - 1至少1个字节 - 文件的最后一个字节 - 可以返回给调用者.)
以下是同一API文档页面中的其他类似实例:
position(...):"将位置设置为大于文件当前大小的值是合法的,但不会更改文件的大小." (应该是'大于或等于'.)
transferTo(...):"如果给定位置大于文件的当前大小,则不传输任何字节." (应该是'大于或等于'.)
read(...):"如果给定位置 …
我的应用程序使用Java类RandomAccessFile通过SeekableByteChannel接口的实现随机读取/写入SD卡上的文件.现在我需要使用新的Lollipop API为Android 5.0重写它.
我找到了唯一的阅读方式:
InputStream inputStream = getContentResolver().openInputStream(uri);
Run Code Online (Sandbox Code Playgroud)
和写:
ParcelFileDescriptor pfd = getActivity().getContentResolver().openFileDescriptor(uri, "w");
FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());
Run Code Online (Sandbox Code Playgroud)
从/到新API中的文件.
我希望能够在某个随机位置设置通道,并将字节读/写到该位置.是否可以在新的SDK 21中执行此操作?新的SDK是否意味着获取频道:
FieInputChannel fieInputChannel = fileInputStream.getChannel();
FieOutputChannel fieOutputChannel = fileOutputStream.getChannel();
Run Code Online (Sandbox Code Playgroud)
或其他一些方法?
filechannel android-sdcard randomaccessfile android-5.0-lollipop documentfile
filechannel ×10
java ×8
inputstream ×2
android ×1
bytebuffer ×1
concurrency ×1
documentfile ×1
filelock ×1
interrupt ×1
java-9 ×1
mp4parser ×1
nio ×1
nio2 ×1