Android 多线程(一)

简介

1. 线程分类

  • 主线程(UI线程) : 处理和界面相关的事情.
  • 子线程 : 处理耗时操作.

Android中规定, 只有在主线程中可以进行UI操作, 但是同时, 主线程中不能进行耗时操作,否则会产生ANR,因此耗时操作必须放到子线程中进行处理.

2. Android中多线程技术

  • Thread
  • AsyncTask : 底层用到了线程池.

    封装了线程池和Handler, 主要使用场景是在子线程中更新UI.

  • IntentService : 底层使用Thread

    内部采用HandlerThread来执行任务,当任务执行完成后IntentService就会自动退出. 从任务执行的角度看,IntentService很像是一个后台线程,其实他是一个Service,正是因为他是服务所以不容易被系统杀死从而保证任务的正常进行.

  • HandlerThread : 底层使用Thread

    具有消息循环的线程, 内部可以使用Handler.

注意一 : 从Android 3.0 开始如果在子线程中进行网络操作就会产生NetworkOnMainThreadException

AsyncTask

AsyncTask 是一个轻量级异步任务类, AsyncTask 封装了Thread和Handler, 通过AsyncTask可以更加方便地执行后台任务以及在主线程中访问UI, 但是AsyncTask并不适合进行特别耗时的后台任务,对于特别耗时的后台任务来说建议使用线程池.

1. AsyncTask 简单使用

  • 创建自定义任务类,继承自AsyncTask.
/**
 * 自定义 AsyncTask 类.
 * P1 : 输入参数类型
 * P2 : 进度类型
 * P3 : 返回值类型
 */
private class DownloadTask extends AsyncTask<Integer, Integer, Integer> {
    /**
     * 1. 主线程
     * 2. 执行任务之前执行, 常用来初始化任务,显示UI等.
     */
    @Override
    protected void onPreExecute() {
        Log.d(TAG, "onPreExecute: " + Thread.currentThread().getId());
    }
    /**
     * 1. 子线程
     * 2. onPreExecute执行后会立即执行这个方法.任务逻辑就是在这个方法中进行的.
     * 3. 可以通过 publicProgress(Progress...) 将任务进度传递到onProgressUpdate.
     * 4. 任务结果通过返回值返回, return.传递到onPostExecute.
     * @param params P2
     * @return P3
     */
    @Override
    protected Integer doInBackground(Integer... params) {
        Log.d(TAG, "doInBackground: " + Thread.currentThread().getId());
        for (int i = 0; i < params[0]; i++) {
            // 检查任务是否被取消
            if (isCancelled()) {
                return -1;
            }
            publishProgress(i);
        }
        return 0;
    }
    /**
     * 1. 主线程
     * 2. publicProgress被调用后会将进度传递到这个方法中.可以在这进行UI更新操作.
     * @param values 任务进度.
     */
    @Override
    protected void onProgressUpdate(Integer... values) {
        Log.d(TAG, "onProgressUpdate: " + Thread.currentThread().getId());
        Log.d(TAG, "onProgressUpdate: " + values[0]);
    }
    /**
     * 任务执行结束后,就会进入这个回调.
     * @param integer 任务结果
     */
    @Override
    protected void onPostExecute(Integer integer) {
        Log.d(TAG, "onPostExecute: " + Thread.currentThread().getId());
        Log.d(TAG, "onPostExecute: " + integer);
    }
    /**
     * 任务取消后调用
     * @param integer doInBackground 返回值.
     */
    @Override
    protected void onCancelled(Integer integer) {
        super.onCancelled(integer);
    }
}
  • 启动任务
// 创建并启动任务
new DownloadTask().execute(5);
  • 输出
//
D/MainActivity: onPreExecute: 1
//
D/MainActivity: doInBackground: 161
//
D/MainActivity: onProgressUpdate: 1 // 线程id
D/MainActivity: onProgressUpdate: 0 // 进度
D/MainActivity: onProgressUpdate: 1
D/MainActivity: onProgressUpdate: 1 // 进度
D/MainActivity: onProgressUpdate: 1
D/MainActivity: onProgressUpdate: 2 // 进度
D/MainActivity: onProgressUpdate: 1
D/MainActivity: onProgressUpdate: 3 // 进度
D/MainActivity: onProgressUpdate: 1
D/MainActivity: onProgressUpdate: 4 // 进度
//
D/MainActivity: onPostExecute: 1 // 线程id
D/MainActivity: onPostExecute: 0 // 结果

代码中已经有了详细的注释,不进行过多解释,通过Log也证实了.这里说下取消任务

AsyncTask 提供了 cancel(boolean) 取消任务.

  1. 参数含义:是否立即中断线程执行,true表示立即终止线程,false表示允许任务完成.
  2. 调用了cancel()onCancelled() 回调会被执行(UI线程), onPostExecute() 不会被执行了.
  3. 调用后 isCancelled 会返回 true.
  4. 建议onInBackground() 中检查 isCancelled 以便尽快结束任务.

2. AsyncTask使用注意

  • AsyncTask这个类必须在主线程中进行加载. 在Android 4.1 以后系统自动完成这一过程.Android 6.0 以后可以在子线程中加载
  • AsyncTask必须在主线程中创建.
  • execute() 必须在UI线程中进行调用.
  • 不要在直接调用 onPreExecute(), onPostExecute(), onInBackground() ,onProgressUpdate() 等方法.
  • 一个AsyncTask 对象 只能调用一次execute方法,如果执行多个任务就创建多个任务.
  • Android3.0 以后 ,AsyncTask用一个线程串行执行任务.

3. AsyncTask的工作原理

  • 首先分析execute() 方法.由于它是直接调用了executeOnExecutor()因此主要分析一下后者
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    // 调用 : executeOnExecutor 
    // sDefaultExecutor 是一个串行的线程池Executor.
    return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    // 检查状态, 此处可用证明为什么一个AsyncTask对象只可以调用一次execute方法.
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            case RUNNING:  // 如果任务已经执行,再次执行就会抛出异常.
                throw new IllegalStateException("Cannot execute task:"
                        + " the task is already running.");
            case FINISHED: // 如果任务已经执行完了,再次执行就会抛出异常.
                throw new IllegalStateException("Cannot execute task:"
                        + " the task has already been executed "
                        + "(a task can be executed only once)");
        }
    }

    // 设置为运行状态.
    mStatus = Status.RUNNING;
    // 首先调用 onPreExecute, 证实了onPreExecute他回调最先执行.
    // onPreExecute在主线程中执行, 这也是AsyncTask的execute()必须在主线程调用的一个的原因.
    onPreExecute();
    // 保存参数
    mWorker.mParams = params;
    // 执行线程
    // mFuture 是一个Runnable.
    exec.execute(mFuture);

    return this;
}

代码中已经有了详解的注释这里就不过多的解释了.

  1. @MainThread 注解说明这两个方法都必须在主线程中执行,因此execute()方法必须在主线程中调用.
  2. if (mStatus != Status.PENDING){...}中的判断说明了,一个AsyncTask对象只可以调用一次execute() 方法.
  3. sDefaultExecutor是一个串行的线程池主要用于任务排队,并不负责任务的实际执行,源码分析如下
// sDefaultExecutor 定义
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
// SERIAL_EXECUTOR : 用于任务排队.
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
// SerialExecutor 串行线程池
private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;
    // 执行Runnable
    public synchronized void execute(final Runnable r) {
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    // 调度下一个.
                    scheduleNext();
                }
            }
        });
        if (mActive == null) {
            scheduleNext();
        }
    }
    // 启动下一个Runnable
    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            // 执行任务.
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}
// mFuture是一个Runnable
private final FutureTask<Result> mFuture;
  1. THREAD_POOL_EXECUTOR 也是一个线程池,负责任务的执行.
// 定义
public static final Executor THREAD_POOL_EXECUTOR;
// 静态加载线程池.
static {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
            sPoolWorkQueue, sThreadFactory);
    threadPoolExecutor.allowCoreThreadTimeOut(true);
    THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
  1. sHandler 负责线程环境的切换.
// 7.0 代码
// 负责线程切换
private static InternalHandler sHandler;
// 获取Handler
private static Handler getHandler() {
    synchronized (AsyncTask.class) {
        if (sHandler == null) {
            sHandler = new InternalHandler();
        }
        return sHandler;
    }
}
// 该类负责实现线程的切换
private static class InternalHandler extends Handler {
    // 7.0 
    public InternalHandler() {
        super(Looper.getMainLooper());
    }
    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
        AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
        switch (msg.what) {
            case MESSAGE_POST_RESULT: // 任务执行结束
                // There is only one result
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS: // 更新进度.
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}
// 任务结束函数
private void finish(Result result) {
    if (isCancelled()) {
        // 任务被取消
        onCancelled(result);
    } else {
        // 任务正常完成
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}

Android 5.0 代码

// 静态加载.
private static final InternalHandler sHandler = new InternalHandler();
private static class InternalHandler extends Handler {
    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
        AsyncTaskResult result = (AsyncTaskResult) msg.obj;
        switch (msg.what) {
            case MESSAGE_POST_RESULT:
                // There is only one result
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}
  1. sHandler 是一个静态的Handler对象,Android 6.0 以前,为了能够将执行环境切换到主线程,这就要求sHandler这个对象必须在主线程中进行创建.
  2. Android 6.0 以后 sHandler 使用了懒加载,因此,在子线程中也可以进行加载AsyncTask.同时在ActivityThread的main() 方法中也去掉了AsyncTask.init(); 也证实了这一点.

5 总结

  • AsyncTask 本质上就是封装了线程池和Handler
  • Android 6.0 以后可以在子线程中加载AsyncTask.

HandlerThread

HandlerThread 继承了Thread ,添加加了Looper, 实现方式如下:

  • 通过Looper.prepare() 来创建消息队列.
  • 通过Looper.loop() 来开启消息循环.
@Override
public void run() {
    mTid = Process.myTid();
    // 创建消息队列.
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    // 子类实现.
    onLooperPrepared();
    // 开启运行循环.
    Looper.loop();
    mTid = -1;
}
  • 普通的Thread主要用于进行耗时操作.HandlerThread内部常见了消息队列,外界需要通过Handler的消息方式通知HandlerThread来执行一个任务.由于HandlerThread的run() 是一个无限循环(Looper.loop()),因此当明确不需要时需要主动调用,quit() 或者 quitSafely() 来终止线程的执行.

关于HandlerThread的使用方式参考IntentService源码

IntentService

1. 简介

  1. IntentService是一种特殊的服务, 他继承自Service并且他是一个抽象类.
  2. IntentService用于后台耗时任务, 任务执行结束后自动停止.
  3. 由于它是一个服务因此有着比普通Thread更高的优先级,不容易被系统杀死.因此他适合执行一些优先级较高的后台任务.

2. 工作原理

  • IntentService 其实就是封装了 HandlerHandlerThread从下面的源码分析中可以看出来.
// 1. Handler
private volatile ServiceHandler mServiceHandler;
// 定义Handler
private final class ServiceHandler extends Handler {
    public ServiceHandler(Looper looper) {
        super(looper);
    }
    @Override
    public void handleMessage(Message msg) {
        // onHandleIntent 这个方法是子类来实现的.
        onHandleIntent((Intent)msg.obj);
        // 停止当前服务.
        stopSelf(msg.arg1);
    }
}
// IntentService 的onCreate方法
@Override
public void onCreate() {
    // TODO: It would be nice to have an option to hold a partial wakelock
    // during processing, and to have a static startService(Context, Intent)
    // method that would launch the service & hand off a wakelock.
    super.onCreate();
    // 创建并启动了HandlerThread.
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();
    // 保存Looper
    mServiceLooper = thread.getLooper();
    // 创建Handler , 并且将创建的Handler 和 HandlerThread 的Looper绑定在一起.
    mServiceHandler = new ServiceHandler(mServiceLooper);
}
  1. mServiceHandler就是一个Handler , 并且他的handleMessage处理逻辑也十分简单,首先调用onHandleIntent将结果返给子类处理. 然后调用stopSelf() 来停止服务.
  2. 在onCreate() 方法中创建了一个HandlerThread 并且将Handler和他绑定.

IntentService工作原理如下:

  1. 在onCreate() 中创建Handler和HandlerThread.并绑定.
  2. onStart() 中通过Handler 发送消息.
  3. 在 Handler中处理调用onHandleIntent()进行耗时操作.
  4. 数据通过Intent进行传输.
  5. 任务执行结束后,后停止服务.
  6. onDestory() 中退出HandlerThread.
工作原理

3. 使用

// 定义
public class LocalIntentService extends IntentService{
    /**
     * 构造方法
     */
    public LocalIntentService() {
        super("123");
    }
    @Override
    protected void onHandleIntent(Intent intent) {
        String action = intent.getStringExtra("task_action");
        Log.d(TAG, "onHandleIntent: " + action);
        // 延时
        SystemClock.sleep(500);
    }
    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy: 服务结束");
        super.onDestroy();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand: ");
        return super.onStartCommand(intent, flags, startId);
    }
}
Intent intent = new Intent(this, LocalIntentService.class);
intent.putExtra("task_action", "任务 1");
startService(intent);
// 任务二
intent.putExtra("task_action", "任务 2");
startService(intent);
SystemClock.sleep(2000);
// 任务三
intent.putExtra("task_action", "任务 3");
startService(intent);

运行代码发现上述三个任务串行执行,最后会停止服务.

Android中的线程池

Android线程池的概念源自于Java的Executor, Executor 是一个接口,ThreadPoolExecutor 是他的实现类.ThreadPoolExecutor提过了一系列的参数来配置线程池.
Android中的线程池主要分为四类.后面会详细介绍.

1. 线程池的好处

  • 重用线程池中的线程, 避免线程的创建和销毁带来的性能开销.
  • 能有效的控制线程池的最大并发数, 避免大量线程之间因为抢夺子系统资源而导致的阻塞现象.
  • 能够对象成进行简单的管理, 并提供定时执行以及定时间隔循环执行的功能.

2. ThreadPoolExecutor

  • 构造方法
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory);
  1. corePoolSize : 线程池的核心线程数,默认情况下核心线程会在线程池中一直存活.如果ThreadPoolExecutor 的 allowCoreThreadTimeOut 属性设置为true, 那么闲置的核心线程会有超时策略,超时时间由keepAliveTime指定.
  2. maximumPoolSize : 线程池最大的线程数.达到最大值后新任务会阻塞.
  3. keepAliveTime : 超时时间.
  4. unit : 超时单位,MILLISECONDS(毫秒),SECONDS(秒), MINUTES(分钟)
  5. workQueue: 线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这里.
  6. threadFactory : 线程工厂, 为线程池提供创建新线程的功能.

除了上面的主要参数以外, ThreadPoolExecuteor还有一个不常用的参数RejectedExecutionHandler handler. 当线程池无法执行新的任务时会调用handler 的rejectedExeception方法来通知调用者, 默认情况下 rejectedExecution 方法会抛出一个rejectedExecutionException异常,具体的可以查看API文档.

  • ThreadPoolExecutor 执行时遵循以下规则:

    • 如果线程池中的线程为达到核心线程的数量, 那么会直接启动一个核心线程来执行任务.
    • 如果线程池中的线程数量已经达到或者超过核心线程数量, 那么任务会插入任务队列中进行排队.
    • 如果过步骤2中无法将任务插入到任务队列,一般是队列已满,这个时候如果线程数量没有达到线程池的最大数量限制,那么会立即启动一个非核心线程来执行任务.
    • 如果步骤3中线程数量已经到了线程池规定的最大值, 那么就拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution() 方法来通知调用者.
  • AsyncTask中的线程池配置

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
static {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
            sPoolWorkQueue, sThreadFactory);
    threadPoolExecutor.allowCoreThreadTimeOut(true);
    THREAD_POOL_EXECUTOR = threadPoolExecutor;
}

3. Android中的四种线程池.

Android中常用有四种线程池,他们都是直接或者间接配置ThreadPoolExecutor 来实现自己的功能的.

3.1 FixedThreadPool

  • 通过Executors 的newFixedThreadPool 方法来创建.
  • 线程数量固定.
  • 线程处于空闲状态时, 线程也不会被回收,除非线程池关闭了.正是这个原因所以它能够更加快速第响应外界请求.
  • 当所有的线程都处于活动状态时, 新任务处于等待状态, 直到有线程空闲.
  • 没有超时机制
  • 没有任务熟练限制.

newFixedThreadPool方法源码

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
}

3.2 CachedThreadPool

  • 通过Executors的newCachedThreadPool 来创建, 它是一种线程数量不定的线程池,他只有非核心线程并且最大线程数为integer.MAX_VALUE.
  • 当线程池中的活动都处于活动状态时,会直接创建新的线程来处理任务.否则就利用空闲线程来处理任务.
  • 超时时间是 60s ,线程闲置60s就会被回收.
  • 这种线程适合大量的耗时较小的任务.

newCachedThreadPool源码

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}

3.3 ScheduledThreadPool

  • 通过Executors的newScheduledThreadPool方法来创建.
  • 核心线程数是固定的,非核心线程数没有限制.当非核心线程一旦闲置就会被立即回收.
  • ScheduledThreadPool主要用来执行定时任务和具有固定周期的重复任务.

newScheduledThreadPool源码

public static ScheduledExecutorService newScheduledThreadPool(
        int corePoolSize, ThreadFactory threadFactory) {
    return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}

3.4 SingleThreadPool

  • 通过Executors的newSingleThreadExecutor方法创建.
  • 只有一个核心线程,他可以确保所有的任务都在同一个线程中顺序执行.
  • 他可以统一所有的外界任务到一个线程中,这样可以避免线程同步问题.

newSingleThreadExecutor源码

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

4. 四种常用线程池的用法

private void threadPoolTest() {
    // 创建任务
    Runnable command = new Runnable() {
        @Override
        public void run() {
            Log.d(TAG, "run: " + Thread.currentThread().getId());
            SystemClock.sleep(2000);
        }
    };
    // FixedThreadPool
    ExecutorService fixed = Executors.newFixedThreadPool(4);
    fixed.execute(command);
    // Cached
    ExecutorService cached = Executors.newCachedThreadPool();
    cached.execute(command);
    // Scheduled
    ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(4);
    // 2000 ms 后执行
    scheduled.schedule(command, 2000, TimeUnit.MILLISECONDS);
    // 延时 10 后每隔 1000ms 执行一次
    scheduled.scheduleAtFixedRate(command, 10, 1000, TimeUnit.MICROSECONDS);
    // Single
    ExecutorService single = Executors.newSingleThreadExecutor();
    single.execute(command);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容

  • Android中的线程 线程,在Android中是非常重要的,主线程处理UI界面,子线程处理耗时操作。如果在主线程...
    shenhuniurou阅读 731评论 0 3
  • 当某个应用组件启动且该应用没有运行其他任何组件时,Android 系统会使用单个执行线程为应用启动新的 Linux...
    耳东CY阅读 1,629评论 0 12
  • 从用途上来说,线程分为主线程和子线程,主线程主要处理和界面相关的事情,子线程则往往用于执行耗时操作。 除了Thre...
    小柏不是大白阅读 591评论 0 3
  • 花在昨夜秋雨中凋零 只剩下一段伤心的枝条 在阴冷的晨风中颤抖 已无法承受现实的残酷 风偶尔吹起记忆的落叶 还没有思...
    风过长河阅读 187评论 0 2
  • 就是想用简书记录生活的点点滴滴,记录曾经走过的每一个角落! 爱生活,爱自由
    IAM四十二阅读 269评论 0 2