Android多线程知识总结

参考安卓进阶之光第四章

进程和线程的关系

  1. 进程是系统资源分配的最小单位,线程的系统调度的最小单位;
  2. 进程之间不能共享资源,线程之间可以共享所在进程的地址空间和资源;
  3. 一个进程中可以有多个线程,一个线程只能属于一个进程;
  4. 进程可以开启线程和其他进程;
  5. 线程都拥有各自的计数器,堆栈和局部变量等属性;

为什么使用多线程

  1. 可以把某一个耗时操作分配到一个单独的线程中去执行,减少程序响应时间;
  2. 线程创建和切换开销小,多线程在数据共享方面效率很高;
  3. 简化程序结构,使程序便于理解和维护;

线程的6种状态

  1. New:新创建状态,线程被创建,还没有调用start方法
  2. Runnable:可运行状态:调用start方法就处于这个状态,可能正在运行也可能没有运行,取决于操作系统;
  3. Blocked:阻塞状态:线程被锁阻塞,暂时不活动
  4. Waiting:等待状态,线程暂时不活动,并且不运行任何代码,等待线程调度器重新激活;
  5. Timed waiting:超时等待状态,在指定的时间自行返回
  6. Terminated:终止状态,当前线程已经执行完毕,线程终止的两种情况:run方法执行完毕或者异常终止

Android多线程知识总结_第1张图片

问题:sleep(),wait(),join()的区别和联系

  • sleep是Thread的静态方法,使当前执行的线程休眠,把执行机会给其他线程,但只会让出CPU执行时间,不会释放锁,可以在任何地方使用;
  • wait是Object里的一个方法,进入到一个与该对象相关的等待池中,同时释放对象锁,并让出CPU资源,待指定时间结束后得到对象锁,只能在同步方法或同步块中使用;
  • join是将指定的线程加入到当前线程,并且等待到指定的线程执行完毕.比如,t.join(1000);  //等待 t 线程,等待时间是1000毫秒。

顺便提一下yield:将线程转为就绪状态,且只会让优先级相同或者更高的线程有执行机会;

创建线程

问题:run和start的区别

  • run只是Thread里面的一个普通方法,调用了run还是在主线程,start是启动子线程的方法

1.继承Thread类

Thread实现了Runnable接口的一个实例

调用start()只是使线程变为可运行态,并不一定立即地执行多线程的代码,什么时候运行是由操作系统决定的;

public class TestThread extends Thread{
    @Override
    public void run() {
        super.run();
        System.out.println(Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        Thread thread = new TestThread();
        thread.start();
    }
}

2.实现Runnable接口

public class TestThread1 implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        TestThread1 testThread1 = new TestThread1();
        Thread thread = new Thread(testThread1);
        thread.start();
    }
}

3.实现Callable接口,重写call()方法

Callable接口实际属于Executor框架中的功能类,比runnable提供了更强大的功能:

  • 可以提供一个返回值
  • call方法可以抛出异常
  • 运行Callable可以拿到一个Future结果,future提供了检查计算是否完成的方法,但调用get()方法时,当前线程就是阻塞,知道call()方法返回结果;
public class TestThread2 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyTestCallable myTestCallable = new MyTestCallable();
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Future future = executorService.submit(myTestCallable);
        System.out.println(future.get());
    }

    public static class MyTestCallable implements Callable{
        @Override
        public Object call() throws Exception {
            return Thread.currentThread().getName();
        }
    }
}

中断

interrupt方法可以用来请求中断线程,调用interrupt方法时,线程的中断标志位将被标为true,线程会不断地检测这个标志位

使用中断来终止线程

public class StopThread {
    public static void main(String[] args) throws InterruptedException {
        MyRunner myRunner = new MyRunner();
        Thread thread = new Thread(myRunner,"mythread");
        thread.start();
        //让主线程睡10ms,使子线程感知中断
        TimeUnit.MILLISECONDS.sleep(10);
        thread.interrupt();
    }

    public static class MyRunner implements Runnable{
        private long i;
        @Override
        public void run() {
            while (!Thread.currentThread().isInterrupted()){
                i++;
                System.out.println("i=" + i + Thread.currentThread().getName() );
            }
            System.out.println("stop");
        }
    }
}

Android多线程知识总结_第2张图片

也可以采用boolean变量控制是否需要停止线程

public class StopThread {
    public static void main(String[] args) throws InterruptedException {
        MyRunner myRunner = new MyRunner();
        Thread thread = new Thread(myRunner,"mythread");
        thread.start();
        //让主线程睡10ms,使子线程感知中断
        TimeUnit.MILLISECONDS.sleep(10);
        myRunner.cancel();
    }

    public static class MyRunner implements Runnable{
        private long i;
        private volatile boolean mark = true;
        @Override
        public void run() {
            while (mark){
                i++;
                System.out.println("i=" + i + Thread.currentThread().getName() );
            }
            System.out.println("stop");
        }

        public void cancel() {
            mark = false;
        }
    }
}

同步

1.重入锁

支持重进入的锁,表示该锁能够支持一个线程对资源的重复加锁,确保任何时刻只有一个线程进入临界区,临界区就是或同一时刻只能有一个任务访问的代码区.一旦一个线程封锁了锁对象,其他任何线程都无法进入Lock语句.把解锁的操作放在finally中是必须的.因为如果在临界区发生了异常,锁无法释放那么其他线程将会永远被阻塞;

条件对象:也叫条件变量,管理那个已经获得了一个锁但是不能做有用工作的线程,因为可能进入临界区后发现,还有某一个条件没有满足;

问题:lock和synchronized的区别:

  • lock是一个借口,synchronized是java的一个关键字;
  • lock必须手动释放锁,synchronized在异常时会自动释放锁;
  • lock可以使用interupt来中断,而sy只能等待锁的释放,无法相应中断;
  • 当竞争激烈时,使用lock
  • lock可以判断锁的状态,sy不可以

举栗说明:一个支付宝转账的例子

public class Alipay {
    private double[] accounts;
    private Lock alipayLock;//重入锁
    private  Condition condition;//引入条件对象

    public Alipay(int n ,double money){
        accounts = new double[n];
        alipayLock = new ReentrantLock();
        condition = alipayLock.newCondition();
        
        for (int i = 0; i < accounts.length; i++) {
            accounts[i] = money;
        }
    }
    public void transfer(int from,int to,int amount) throws InterruptedException {
        alipayLock.lock();
        try {
            while (accounts[from]

 使用synchronized的写法

public synchronized void transfer(int from, int to, int amount) throws InterruptedException {
        while (accounts[from] < amount) {//钱不够转账
            wait();
        }
        accounts[from] -= amount;
        accounts[to] += amount;
        notifyAll();
    }

wait方法将一个线程添加到等待集中

notifyAll等价于condition.signalAll();

原子性,可见性和有序性

  • 原子性:比如x=3就是原子性操作,x++就不是,因为x++是先读取x的值,再x+1
  • 可见性:一个线程修改的状态对于另一个线程是可见的,立马能看到的,一个变量被volatile修饰后,就保证了可见性;
  • 有序性:java内存模型允许编译器和处理器对指令进行重排序,这不会影响到单线程执行的正确性,但是会影响到多线程并发执行的正确性,所以可以通过volatile,synchronized,lock保证有序性

volatile

声明一个域为volatile,编译器和虚拟记就知道该域可能被另外一个线程并发更新;

java中的堆内存是所有线程的共享的内存区域,存在内存可见性的问题;每个线程都有一个私有的本地内存,本地内存存储了该线程共享变量的副本;

volatile不保证原子性,保证了有序性(在volatile后面的语句不能在它之前执行)

volatile的两种使用场景:

1.用于标志位

2.用于单例模式中的双重检查模式

阻塞队列

常用于生产者和消费者的场景,他有两个阻塞场景

  • 队列中没有数据,消费者端的所有线程会被自动阻塞
  • 队列中数据满了,生产者端的所有线程会被自动阻塞

BolckQueue的核心方法:

  • offer(object)加入队列中,可以容纳就返回true,本方法不阻塞当前线程
  • offer(E o,long time,TimeUnit unit) :设定等待的时间,在指定的时间内无法加入,则返回失败
  • put(anObject) :加入到队列里,没有空间,调用此方法的线程被阻断
  • poll(time) :在规定时间内取走队列中排在首位的对象,取不到返回null
  • take():取走排在首位的对象,如果为空,就等待等待状态
  • drainTo():一次性获取所有可用的对象

常用的阻塞队列:

  • ArrayBlockingQueue:由数组组成的有界阻塞队列
  • LinkedBlockingQueue:由链表组成的有界阻塞队列
  • PriorityBlockingQueue:支持优先级排序的无界阻塞队列

实现生产者-消费者模式的两种方法

1.非阻塞队列

public class Test {
    private int queueSize = 10;
    private PriorityQueue queue = new PriorityQueue<>(queueSize);

    public static void main(String[] args) {
        Test test = new Test();
        Consumer consumer = test.new Consumer();
        Producer producer = test.new Producer();
        producer.start();
        consumer.start();
    }

    class Consumer extends Thread {
        @Override
        public void run() {
            while (true) {
                synchronized (queue) {
                    while (queue.size() == 0) {
                        try {
                            System.out.println("队列空");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notify();
                        }
                    }
                    //每次移走队首元素
                    queue.poll();
                    queue.notify();
                }
            }
        }
    }

    class Producer extends Thread {
        @Override
        public void run() {
            synchronized (queue) {
                while (queue.size() == queueSize) {
                    try {
                        System.out.println("队列满了");
                        queue.wait();
                    }catch (InterruptedException e){
                        e.printStackTrace();
                        queue.notify();
                    }
                }
                //每次插入一个元素
                queue.offer(1);
                queue.notify();
            }
        }
    }
}

2.阻塞队列

public class Test1 {
    private int queueSize = 10;
    private ArrayBlockingQueue queue =
            new ArrayBlockingQueue(queueSize);

    public static void main(String[] args) {
        Test1 test1 = new Test1();
        Consumer consumer = test1.new Consumer();
        Producer producer = test1.new Producer();
        producer.start();
        consumer.start();
    }

    class Consumer extends Thread{
        @Override
        public void run() {
            while (true){
                try {
                    queue.take();
                    System.out.println(queue.size());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class Producer extends Thread{
        @Override
        public void run() {
            super.run();
            while (true){
                try {
                    queue.put(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }


}

线程池

ThreadPoolExecutor的构造方法参数:

corePoolSize:核心线程数,默认下线程池是空的,任务提交时才会创建线程,如果当前运行的线程数少于corePoolSize,则创建新线程来处理任务

workQueue:任务队列,如果当前线程数大于corePoolSize,则将任务添加到任务队列中,队列是阻塞队列

maximumPoolSize:线程池允许创建的最大线程数,如果任务队列满了并且线程数少于maximumPoolSize,线程池任然会创建新的线程来处理任务

keepAliveTime:非核心线程闲置的超时时间;

TimeUnit:KeepAlive的时间单位

ThreadFactory:线程工厂,一般无需设置

RejectExecutionHandler:饱和策略,任务队列和线程池都满了采取的策略,默认为abordPolicy,并抛出异常

问题:如果最大线程池是128,任务队列容量是10,最多容纳多少任务?

138个,因为首先是线程池和任务队列的关系是线程池从任务队列中去取任务,当核心线程池不满时,就创建新核心线程去处理任务,核心线程池满了,就先把任务放在任务队列中等待处理,任务队列也满了,就在非核心线程池中新建非核心线程去处理任务,非核心线程的存活时间是keepAliveTime,非核心线程池也满了,就是说达到max值了,就执行饱和策略

线程池的种类

1.FixedThreadPool

只有核心线程,没有非核心线程

2.CachedThreadPool

没有核心线程,非核心线程是无界的,适合大量的需要立即处理并且耗时较少的任务

3.SingleThreadPool

单个工作的线程,只有一个核心线程,能确保所有任务都在一个线程中按照顺序执行

4.ScheduledThreadPool

定时和周期性任务的线程池

AsyncTask

是一个抽象的泛型类,有三个泛型参数,分别是

  • Params:参数类型
  • Process:后台任务执行进度的类型
  • Result:返回结果的类型

四个核心方法:

  • onPreExecute:在主线程中执行,一般用于在任务执行前的准备工作
  • doInBackground(parms...params):在主线程中执行,用来执行较为耗时的操作,执行线程中可以调用publishProgress(Progress...values)来更新进度信息;
  • onProgressUpdate(Progress...values):在主线程中执行,当调用publishProgress(Progress...values),此方法会将进度更新到UI组件上
  • onPostExecute(Result result):在主线程中执行,后台任务执行完成会被执行,result的值是从doInBackGround中返回的值

举栗说明:参考https://blog.csdn.net/yixingling/article/details/79517048




    
    
    

 

public class AsyncTaskActivity extends AppCompatActivity {
    private EditText et;
    private TextView tv;
    private Button bt;
    private int time = 0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_async_task);
        bindID();
    }

    private void bindID() {
        et = findViewById(R.id.et_asyc);
        tv = findViewById(R.id.tv_asyc);
        bt = findViewById(R.id.bt_asyc);
        bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!TextUtils.isEmpty(et.getText())) {
                    time = Integer.parseInt(et.getText().toString());
                    new MyTask().execute(time);
                }else {
                    Toast.makeText(AsyncTaskActivity.this, "请输入起始值", Toast.LENGTH_SHORT).show();
                }
            }
        });
    }

     @SuppressLint("SetTextI18n")
     class MyTask extends AsyncTask{

        @Override
        protected String doInBackground(Integer... integers) {
            for (int i=integers[0];i>=0;i--){
                try {
                    Thread.sleep(1000);
                    publishProgress(i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return "计时结束";
        }

         @Override
         protected void onProgressUpdate(Integer... values) {
             super.onProgressUpdate(values);
             tv.setText(values[0]+"");
         }

         @Override
         protected void onPostExecute(String s) {
             super.onPostExecute(s);
             tv.setText(s);
         }
     }
}

举栗:模拟下载




    

    

    
public class AsyncTaskActivity extends AppCompatActivity implements View.OnClickListener {
    private Button btn1;
    private Button btn2;
    private ProgressBar pro1;
    private ProgressBar pro2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_async_task);
        bindID();
    }

    private void bindID() {
        btn1 = findViewById(R.id.bt1);
        btn2 = findViewById(R.id.bt2);
        pro1 = findViewById(R.id.pb_asyc);
        pro2 = findViewById(R.id.pb2_asyc);

        btn1.setOnClickListener(this);
        btn2.setOnClickListener(this);

    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.bt1:
                DownloadTask downloadTask1 = new DownloadTask();
                //执行executeOnExecuter()方法线程并行运行,不过同一时间只能启动五个线程
                downloadTask1.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, 1);
                btn1.setText("下载中...");
                btn1.setEnabled(false);//按钮变为不可按
                break;
            case R.id.bt2:
                DownloadTask downloadTask2 = new DownloadTask();
                downloadTask2.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, 2);
                btn2.setText("下载中...");
                btn2.setEnabled(false);
                break;
            default:
                break;
        }
    }

    class DownloadTask extends AsyncTask {

        @Override
        protected Integer doInBackground(Integer... integers) {
            for (int i=0;i<=10;i++){
                try {
                    Thread.sleep(1000);
                    publishProgress(integers[0],i);//哪一个,值,注意这里的参数是[0]
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return integers[0]; //把下载的第几个传过去
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            switch (values[0]){
                case 1:
                    pro1.setProgress(values[1]*10);
                    break;
                case 2:
                    pro2.setProgress(values[1]*10);
                    break;
            }
        }

        @Override
        protected void onPostExecute(Integer integer) {
            super.onPostExecute(integer);
            switch (integer){
                case 1:
                    btn1.setText("下载完成");
                    btn1.setEnabled(true);
                    break;
                case 2:
                    btn2.setText("下载完成");
                    btn2.setEnabled(true);
                    break;
            }
        }
    }

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(Android,安卓进阶之光笔记,android面试)