• Android多线程学习:线程


    一、概念

    进程:系统资源分配的基本单位,进程之间相互独立,不能直接访问其他进程的地址空间

    线程CPU调度的基本单位,线程之间共享所在进程的资源,包括共享内存,公有数据,全局变量等。

    后台线程:后台线程又称为守护线程(Daemon Thread),JVM的垃圾回收线程就是典型的后台线程。

    举例记忆:以下纯属本人瞎编,方便记忆

    • 进程就是一个鞋子工厂,鞋子由鞋带、鞋底、鞋帮三部分组成。线程就是工厂下的流水线,一条流水线做鞋带,一条流水线做鞋底,一条流水线做鞋帮,最后再把做好的组件组装起来变成一个鞋子。
    • 我们会把原材料直接提供给工厂,工厂统一接收而不是里面具体的某个流水线。所以工厂(进程)就是我们系统分配资源的最小单位。
    • 如果想做出鞋子,至少要开启一个流水线工作,这个流水线可以先做鞋带,在做鞋帮,在做鞋底,最后再组装成鞋子,如果没有流水线工作,一双鞋子也做不出来。所以流水线是系统可调度执行的基本单位。
    • 工厂包含多条流水线,即进程包含多个线程,而多条流水线上的工人又共享工厂里的食堂、厕所、宿舍,线程之间共享所在进程的资源。

    二、线程三种实现

    1、继承Thread类

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //3、创实例,并调用start()方法开启线程。
            new MyThread().start();
            new MyThread().start();
        }
    
        //1、定义一个类MyThread继承Thread,并重写run方法
        class MyThread extends Thread {
    
            @Override
            public void run() {
                //2、将执行的代码写在run方法中。
                Log.d(TAG, "线程名字:" + Thread.currentThread().getName());
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    2、实现Runnable接口

    public class MainActivity extends AppCompatActivity {
        public static final String TAG = MainActivity.class.getSimpleName();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //3、创建Thread对象, 传入MyRunnable的实例,并调用start()方法开启线程。
            Thread thread = new Thread(new MyRunnable());
            thread.start();
            Thread thread1 = new Thread(new MyRunnable());
            thread1.start();
        }
    
        // 1、定义一个类MyRunnable实现Runnable接口,并重写run方法。
        class MyRunnable implements Runnable {
            public void run() {
                //2、将执行的代码写在run方法中。
                Log.d(TAG, "线程名字:" + Thread.currentThread().getName());
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    3 、通过Callable和Future创建有返回值的多线程

    public class MainActivity extends AppCompatActivity {
        private final String TAG = this.getClass().getSimpleName();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //3、创建线程池对象,调用submit()方法执行MyCallable任务,并返回Future对象
            ExecutorService pool = Executors.newSingleThreadExecutor();
            Future<Integer> f1 = pool.submit(new MyCallable());
            //4、调用Future对象的get()方法获取call()方法执行完后的值
            try {
                Log.d(TAG, "sum = " + f1.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            //5、关闭线程池
            pool.shutdown();
        }
    
        //1、自定义一个类MyCallable实现Callable接口,并重写call()方法
        public class MyCallable implements Callable<Integer> {
            @Override
            public Integer call() throws Exception {
                //2、将要执行的代码写在call()方法中
                int sum = 0;
                for (int i = 0; i <= 100; i++) {
                    sum += i;
                }
                return sum;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    三、线程的生命周期

    1、线程的生命周期包括:新建New,就绪Runnable,运行Running,阻塞Blocked,和死亡Dead,5种状态。

    新建:程序使用new关键字之后,该线程就处于新建状态,jvm为其分配内存,并初始化成员变量。

    就绪:程序调用start() 方法之后,该线程就处于就绪状态,jvm为其创建方法调用栈和PC计数器。

    运行:如果就绪状态的线程获得了CPU,那么程序就处于运行状态。

    阻塞:指一个线程在执行过程中暂停,以等待某个条件的触发。

    死亡:线程执行体执行结束,以及抛出一个未捕获的ExceptionError,或者直接调用stop()方法结束该线程。可以通过线程对象的isAlive()方法,来判断线程对象的状态。

    2、生命周期转换

    (1)运行到阻塞:

    • 线程调用sleep()方法,主动放弃所占有的CPU资源;
    • 线程调用了一个阻塞式IO方法,在该方法返回之前,该线程被阻塞;
    • 线程试图获得一个同步监视器,但是该同步监视器被其他线程所持有;
    • 线程等待某个通知notify()notify()通常与wait()配合使用;
    • 线程调用suspend(),挂起,该方法容易造成死锁,不建议使用。

    (2)阻塞到就绪:

    • sleep()方法的线程经过了指定的sleep的时间;
    • 阻塞式IO方法返回值;
    • 成功获得了同步监视器;
    • 线程获得了其他线程发出的通知,被唤醒;
    • 挂起的线程调用了resume()方法恢复。

    (3)状态转换图
    线程生命周期.png

    四、线程常用方法

    1、setPriority(int newPriority) - 设置线程优先级

    • 设置线程的优先级来改变线程争抢到时间片的概率,优先级高的争抢到时间片的概率越大;
    • 优先级的取值范围:1~10,默认为5,数字越大,优先级越高;
    • 这个方法的调用必须在start之前,否则没有任何意义;
    • 使用方法getPriority(),获取当前线程优先级。

    2、setDeamon(boolean b) - 设置后台线程

    • 如果所有的前台线程死亡,那么后台线程会自动死亡;
    • 默认情况下所有的线程都是前台线程;
    • 这个方法的调用必须在start之前。

    3、sleep(long millis) - 设置线程休眠

    • 让当前线程暂停millis毫秒,并进入阻塞状态,睡眠状态的线程不会释放同步监视器,在此期间该线程不会获得执行的机会;
    • 使用sleep方法时需要捕捉InterruptedException或者抛出该异常。

    4、interrupt() - 线程中断

    • 表示的并不是将线程结束,而是表示清除阻塞状态;
    • 中断线程操作实质上是修改了一下中断标示位为true
    • 如果线程处于阻塞状态,抛出异常InterruptedException
    public class MainActivity extends AppCompatActivity {
        public static final String TAG = MainActivity.class.getSimpleName();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Thread thread = new Thread(new MyRunnable());
            thread.start();
    
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //打断休眠线程
            thread.interrupt();
        }
    
        class MyRunnable implements Runnable {
            public void run() {
                try {
                    Log.i(TAG, "----开始休眠-----");
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    Log.i(TAG, "----线程中断-----");
                }
                Log.i(TAG, "----休眠后执行-----");
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    执行结果:报异常,提示中断,取消线程阻塞,执行休眠后操作。

    interrupt.PNG

    public class MainActivity extends AppCompatActivity {
        public static final String TAG = MainActivity.class.getSimpleName();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Thread thread = new Thread(new MyRunnable());
            thread.start();
            thread.interrupt();
        }
    
        class MyRunnable implements Runnable {
            public void run() {
                for (int i = 0; i < 5; i ++){
                    Log.i(TAG,"打印: " + i);
                    if (Thread.interrupted()){
                        Log.i(TAG, "----线程被中断了-----");
                    }
                }
                Log.i(TAG, "----执行完了- interrupt state: " + Thread.interrupted());
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    执行结果:interrupted()只有主线程调用的时候才会是truefor循环结束后再次执行是falseinterrupt2.PNG

    5、join() - 线程合并

    • 在执行原来的线程的过程中,如果遇到了合并线程,则优先执行合并进来的线程,当合并线程执行完毕之后,再接着执行原来的线程;
    • 调用join方法之前,一定要将线程start
    • join(0)的意思不是A线程等待B线程0秒,而是A线程等待B线程无限时间,直到B线程执行完毕,即join(0)等价于join()
    public class MainActivity extends AppCompatActivity {
       public static final String TAG = MainActivity.class.getSimpleName();
    
       @Override
       protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_main);
           Thread thread1 = new Thread(new JoinRunnable());
           thread1.start();
    
           for (int i = 0; i < 5; i++) {
           //主线程执行1的时候,把执行权让给了子线程
               if (i == 1) {
                   try {
                       thread1.join();
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
               Log.i(TAG, Thread.currentThread().getName() + "正在运行....");
           }
       }
    
    
       class JoinRunnable implements Runnable {
    
           @Override
           public void run() {
               for (int i = 0; i < 3; i++) {
                   try {
                       Thread.sleep(1000);
                       Log.i(TAG, Thread.currentThread().getName() + "正在运行....");
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
           }
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    执行结果:
    join3.PNG
    6、yield() - 线程让步

    • 使得正在执行的线程暂停,但不会阻塞线程,释放自己拥有的CPU,线程进入就绪状态。
    • 只有优先级与当前线程相同,或者优先级比当前线程更高的线程才有可能获得执行机会,但是可能是当前线程又进入到“运行状态”继续运行。
    public class MainActivity extends AppCompatActivity {
        public static final String TAG = "test";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Thread thread1 = new Thread(new MyRunnable());
            thread1.start();
        }
    
    
        class MyRunnable implements Runnable {
    
            @Override
            public void run() {
                long begainTime = System.currentTimeMillis();
                int count = 0;
                for (int i = 0; i < 5000000; i++) {
                    //结果2需要将下面注释放开
                    //Thread.yield();
                    count = count + (i + 1);
                }
                long endTime = System.currentTimeMillis();
                Log.i(TAG, "用时:" + (endTime - begainTime) + "ms");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    运行结果1:
    yield1.PNG

    运行结果2,执行时放开Thread.yield()
    yield3.PNG

    • yield()也不会释放锁标志,示例如下:
    public class MainActivity extends AppCompatActivity {
        public static final String TAG = "test";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Thread thread1 = new Thread(new MyRunnable());
            Thread thread2 = new Thread(new MyRunnable());
            thread1.start();
            thread2.start();
        }
    
        static class MyRunnable implements Runnable {
            private static Object obj = new Object();
    
            @Override
            public void run() {
                synchronized (obj) {
                    for(int i = 0;i < 5;i++) {
                        Log.i(TAG, Thread.currentThread().getName() + "正在执行i: " + i);
                        if(i == 2) {
                            Thread.currentThread().yield();
                        }
                    }
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    运行结果,Thread-2获取锁以后,就算yield,也没释放锁:
    yield4.PNG
    7、stop() - 线程停止

    • 官方不建议使用该方法,因为stop不安全,stop会解除由线程获取的所有锁定,当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止,假如一个线程正在执行:synchronized void { x = 3; y = 4;} 由于方法是同步的,多个线程访问时总能保证x,y被同时赋值,而如果一个线程正在执行到x = 3;时,被调用了stop()方法,即使在同步块中,它也会马上stop了,这样就产生了不完整的脏数据。

    参考文章:
    对进程、线程、多线程、线程池的理解
    带你通俗易懂的理解——线程、多线程与线程池
    线程类的常见方法介绍
    java多线程以及Android多线程

  • 相关阅读:
    第7章 - 多无人机系统的协同控制 --> 实验验证
    Mac上几款免费的MySql客户端
    面向对象 || 设计模式.未
    云上攻防--云原生&&Docker逃逸--特权逃逸--危险挂载--漏洞逃逸
    剑指Offer 36.二叉搜索树与双向链表 中序遍历
    力扣 757. 设置交集大小至少为2
    DPDK初始化
    C++11 - 4 -万能引用
    Android开发-Mac Android开发环境搭建(Android Studio Mac环境详细安装教程,适合新手)...
    python+appium
  • 原文地址:https://blog.csdn.net/kongqwesd12/article/details/133687332