Android中的线程

前言

我一直在想一个人总是感觉自己没有做成功过什么事,从小到大什么都普普通通这算不算是失败的人生。

线程

首先线程是什么东西呢,线程是CPU的资源调度的最小单位,CPU分配给进程的资源,进程在将分配到的资源分配给一个一个的线程使用。

Android中创建线程

第一种方式:通过new Thread的方式 或者是实现一个Runnable接口来创建线程

new Thread(() -> System.out.println("当前的线程:" + Thread.currentThread().getName())).start();

这种方式对大家来说应该很熟悉了 ,就不多说了。

使用AsyncTask的方式来创建线程

  • 使用场景:适用于需要执行的任务的进度,通过获得的进度来更新UI。
  • 缺点:AsyncTask的生命周期和宿主的生命周期不同步,有可能产生内存泄漏。
  • 备注: 默认的情况下AsyncTask的中的任务是串行执行的一个,如果一个任务一直执行不完,后边的任务可能一直无法执行,如果想让AsyncTask中的任务并发执行可以通过指定 THREAD_POOL_EXECUTOR实现。
 class MyAsyncTask extends AsyncTask<String, Integer, String> {

            @Override
            protected String doInBackground(String... strings) {
                for (int i = 0; i < 10; i++) {
                    publishProgress(i * 10);
                }
                return strings[0];
            }

            @Override
            protected void onProgressUpdate(Integer... values) {
                Log.e(TAG, "onProgressUpdate:" + values[0]);
            }

            @Override
            protected void onPostExecute(String s) {
                Log.e(TAG, "onPostExecute:" + s);
            }
        }
        //需要知道进度 ,并更新UI
        MyAsyncTask task = new MyAsyncTask();
//        task.execute("execute my task");
        //所有任务串行执行 即先来后到 如果有一个任务一直执行不完 那后边的任务将我发执行
        AsyncTask.execute(() -> Log.e(TAG, "run AsyncTask is Running"));
        //AsyncTask中的任务可以并发执行 适用与并发任务
        AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> Log.e(TAG, "run AsyncTask THREAD_POOL_EXECUTOR is Running"));
        for (int i = 0; i < 10; i++) {
//            AsyncTask.execute(() -> Log.e(TAG, "run AsyncTask is Running" + System.currentTimeMillis()));
        }

通过HandlerThread来创建线程

  • 使用场景:适用于主线程和工作需要通信的场景
  • 特点:所有的任务是串行执行的,执行结束后不会自动释放资源
  • 缺点:因为执行结束之后不会自动释放资源,所以容易产生内存泄漏
 class LooperThread extends Thread {
            private Looper looper;

            /****
             * 1 当主线程中调用getLooper的时候
             *   以为looper是null的所以主线程会进入wait状态
             * 2 等到执行run方法的时候得到looper之后在通过notify唤醒主线程
             * @return
             */
            public Looper getLooper() {
                //此处必须加锁 因为当前的线程就是一种资源
                //此处假设不加锁 就可以有很多的对象来调用wait()  就会有多个地方获取looper对消息做处理
                //就会出现问题是
                synchronized (this) {
                    if (looper == null && isAlive()) {
                        try {
                            wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                return looper;
            }

            @Override
            public void run() {
                super.run();
                //获取looper
                Looper.prepare();
                synchronized (this) {
                    //保存looper
                    looper = Looper.myLooper();
                    notify();
                }
                //启动loop 能遍历消息队列
                Looper.loop();
            }
        }
        LooperThread looperThread = new LooperThread();
        looperThread.start();
        Handler handler = new Handler(looperThread.getLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                if (msg.what == 2) {
                    Log.e(TAG, Thread.currentThread().getName());
                    Log.e(TAG, "处理主线程发送过来的消息" + msg.what);
                }
            }
        };
        handler.sendEmptyMessage(MSG_WHAT_2);

     //适用于主线程和工作线程需要进行通信的场景,所有任务串行执行 轮训
        //执行结束后不会自动释放资源 ,所有也有可能产生内存泄漏
        //eg 代码实现的是主线程到工作线程发消息的功能
        HandlerThread handlerThread = new HandlerThread("handler-thread");
        handlerThread.start();
        ThreadHandler threadHandler = new ThreadHandler(handlerThread.getLooper());
        threadHandler.sendEmptyMessage(MSG_WHAT_1);

线程的状态

一个线程在执行内部的任务逻辑的时候是在不同的状态之间进行切换的,下面详细说一下

初始状态:初始状态就是线程刚被创建出来,还没有执行他的onStart()方法,这个时候的状态成为初始状态。

运行状态:我们把运行中的和就绪状态的线程都称为运行态。

阻塞状态:表示线程阻塞于锁。

等待状态:需要其他的线程来唤醒的状态。

超时等待状态:在指定的时间超时后自动返回。

终止状态:表示当前线程已经执行完毕。

说完线程的状态那我们就会好奇,线程的状态是怎么变化的呢 是自动的变化的还是我们可以通过api来进行操作呢,api肯定是提供了一系列的方法让我们可以对线程的执行的过程进行管理的所以我们接下来要说说关于线程状态切换的API方法。

  • wait():一个线程调用了wait()方法就会进入等待池,释放掉他锁持有的资源,我们可以通过notify和notifyAll或者超时等待来进行唤醒当前的线程,让他重新竞争CPU的资源进而来运行。
  • join():join()方法理解为插队的意思,等待目标线程执行结束之后在执行。
  • yield():暂停正在执行的线程对象,但是不释放资源,让同优先级的或者是高优先级的线程有机会执行。
  • sheep():使调用的线程进入休眠状态,在synchronized内执行sleep会休眠但是不会释放锁。
  • notify():唤醒一个线程,但是不会立刻释放锁资源。

线程间的通信

在Android应用中我们的应用程序中会有多个线程,为了让线程们协调工作,就需要线程的通信,在android中可以通过Handler在工作线程和主线程中间发消息进行通信。

对于主线程向工作线程发消息我们每天都在用,我们可以在Activity中创建Handler同时复写他的handlerMessage()对消息进行处理,接下来我们就可以使用这个handler调用sendMessage()在工作线程中向主线程发消息了,本文在使用HandlerThread中已经展示了如何在主线程向工作线程发消息。

线程安全

使用Synchronized关键字保证线程安全

  • 锁方法 :加载方法上 为获取对象锁的其他线程都无法访问
  • 锁Class对象:加载static方法前相当于给Class对象加锁,哪怕不同的java对象也要排队访问
  • 锁代码块:未获取对象锁的其他线程能访问同步代码块之外的代码

使用Synchronized关键的优点是即使出现了异常资源也能得到释放,缺点是必须等待一个锁执行结束或者是出现异常才能进行锁的释放,不能中途释放,另外也不知道有多少线程在竞争锁,已经成功与否,设置锁的条件比较单一。

public class SynchronizedDemo {
    static List<String> tickets = new ArrayList<>();

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            tickets.add("票_" + (i + 1));
        }
        sellTickets();
    }

    private static void sellTickets() {

        final SynchronizedTestDemo synchronizedTestDemo = new SynchronizedTestDemo();
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                synchronizedTestDemo.printThreadName();
//                new SynchronizedTestDemo().printThreadName();
            }).start();
        }
    }

    static class SynchronizedTestDemo {
        // synchronized加载一个普通的方法上是对同一个对象操作的时候的场景处理的
        //不同的对象的场景还是无法保证
        // synchronized加载一个静态的方法上是对Class对象加锁
        /* static synchronized*/ void printThreadName() {
            String name = Thread.currentThread().getName();
            System.out.println("买票人" + name + "准备好了...");
            synchronized (this) {
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            System.out.println("买票人" + name + "买到的票是..." + tickets.remove(0));

        }

    }
}

使用ReentrantLock关键字保证线程安全
ReentrantLock的常用方法

  • void lock 获取锁 获取不到会阻塞
  • boolean tryLock() 尝试获取锁 成功返回true
  • boolean tryLock(time,timeUnit) 在一定的时间内不断的尝试获取锁
  • void lockInterruptibility() 可以通过Thread的interrupt()退出锁的竞争
    1 可重入锁:可以在一个线程中不释放锁的情况下多次获取锁

2 非公平锁:默认是非公平锁,允许线程插队,避免线程 唤醒,等操作消耗性能

//基本用法
public class ReentrantLockDemo {


    static class ReentrantLockTask {
        ReentrantLock reentrantLock = new ReentrantLock();
        void buyTicket() {
            String name = Thread.currentThread().getName();
            try {
                reentrantLock.lock();
                System.out.println(name + ":准备好了");
                Thread.sleep(1000);
                System.out.println(name + ":买好了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        ReentrantLockTask reentrantLockTask = new ReentrantLockTask();
        Runnable runnable = () -> reentrantLockTask.buyTicket();
        for (int i = 0; i < 10; i++) {
            new Thread(runnable).start();
        }
    }

}
//公平锁非公平锁用法
public class ReentrantLockDemo2 {
    static class ReentrantLockTask {
        //公平锁:交易场景
        //非公平锁:场景比比都是
        ReentrantLock reentrantLock = new ReentrantLock(true);

        void print() {
            String name = Thread.currentThread().getName();
            try {
                reentrantLock.lock();
                System.out.println(name + ":第一次打印");
                Thread.sleep(1000);
                reentrantLock.unlock();
                reentrantLock.lock();
                System.out.println(name + ":第二次打印");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        ReentrantLockTask reentrantLockTask = new ReentrantLockTask();
        Runnable runnable = () -> reentrantLockTask.print();
        for (int i = 0; i < 10; i++) {
            new Thread(runnable).start();
        }
    }

}
//condition条件
public class ReentrantLockDemo3 {
    static class ReentrantLockTask {
        private Condition worker1, worker2;
        ReentrantLock reentrantLock = new ReentrantLock(true);
        volatile int flag = 0;

        public ReentrantLockTask() {
            worker1 = reentrantLock.newCondition();
            worker2 = reentrantLock.newCondition();
        }

        void worker1() {
            try {
                reentrantLock.lock();
                if (flag == 0 || flag % 2 == 0) {
                    System.out.println("worker1 无砖可搬,休息会");
                    worker1.await();
                }
                System.out.println("worker1 搬的砖是:" + flag);
                flag = 0;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        }

        void worker2() {
            try {
                reentrantLock.lock();
                if (flag == 0 || flag % 1 == 0) {
                    System.out.println("worker1 无砖可搬,休息会");
                    worker2.await();
                }
                System.out.println("worker1 搬的砖是:" + flag);
                flag = 0;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        }


        void boss() {
            try {
                reentrantLock.lock();
                flag = new Random().nextInt(100);
                if (flag % 2 == 0) {
                    worker2.signal();
                    System.out.println("生产出来的砖 唤醒worker2 来搬" + flag);
                } else {
                    worker1.signal();
                    System.out.println("生产出来的砖 唤醒worker1 来搬" + flag);

                }
            } finally {
                reentrantLock.unlock();
            }
        }
    }


    public static void main(String[] args) {
        ReentrantLockTask reentrantLockTask = new ReentrantLockTask();
        new Thread(() -> {
            while (true) {
                reentrantLockTask.worker1();
            }
        }).start();
        new Thread(() -> {
            while (true) {
                reentrantLockTask.worker2();
            }
        }).start();
        new Thread(() -> {
            while (true) {
                reentrantLockTask.boss();
            }
        }).start();
    }
}
//共享锁和排他锁使用
public class ReentrantLockDemo4 {

    public static void main(String[] args) {
        ReentrantLockTask reentrantLockTask = new ReentrantLockTask();
        for (int i = 0; i < 4; i++) {
            new Thread(() -> reentrantLockTask.read()).start();
        }
        for (int i = 0; i < 4; i++) {
            new Thread(() -> reentrantLockTask.write()).start();
        }
    }

    static class ReentrantLockTask {
        //共享锁 同一时间多个线程可以同时访问
        ReentrantReadWriteLock.ReadLock readLock;
        //排他锁 同一时间只有一个线程能访问处理
        ReentrantReadWriteLock.WriteLock writeLock;

        ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();

        public ReentrantLockTask() {
            //并发读
            readLock = reentrantReadWriteLock.readLock();
            //线程安全写
            writeLock = reentrantReadWriteLock.writeLock();
        }

        void read() {
            String name = Thread.currentThread().getName();
            try {
                readLock.lock();
                System.out.println("线程" + name + "正在读取数据...");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                readLock.unlock();
                System.out.println("线程" + name + "释放了读锁...");

            }
        }

        void write() {
            String name = Thread.currentThread().getName();
            try {
                writeLock.lock();
                System.out.println("线程" + name + "正在写入数据...");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                writeLock.unlock();
                System.out.println("线程" + name + "释放了写锁...");

            }
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值