Android多线程-基础

725 阅读4分钟

实现方式

  1. 继承Thread类,并重写run方法
  2. 实现Runnable,并作为参数构造Thread

两种方式最终都是通过Thread的run方法进行调用

Thread.java
@Override
    public void run() {
      //target为传入的Runnable对象
        if (target != null) {
            target.run();
        }
    }

线程开启后会执行其中的run方法,执行完后线程结束,分别对应了线程的NEW、RUNNABLE、TERMINATED状态

Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
              //运行中,state为RUNNABLE
                System.out.println(Thread.currentThread().getName() + "--state:" + Thread.currentThread().getState());
            }
        }, "Thread1");
      //创建未start,state为NEW
        System.out.println(t1.getName() + "--state:" + t1.getState());
        t1.start();
​
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
      //自动结束,state为TERMINATED
        System.out.println(t1.getName() + "--state:" + t1.getState());

打印结果:

Thread1--state:NEW Thread1--state:RUNNABLE Thread1--state:TERMINATED

线程的操作

常用的线程操作方法有:

wait、notify、notifyAll

这三个方法是定义在Object类中的,即所有的类都可以作为lock

当一段代码块以lock对象为锁时,即意味着访问该代码段的所有线程需要先获得lock锁才能执行其中代码,下面代码中Thread1、Thread2都调用了objectLock(),Thread1先拿到了锁,Thread2只能等到其释放再获取并执行

输出如下:

Thread1--start Thread1--get lock Thread2--start Thread1--release lock Thread2--get lock Thread2--release lock

    private Object lock = new Object();
    private void testLock() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                objectLock();
            }
        }, "Thread1").start();
        System.out.println("Thread1" + "--start");
​
        new Thread(new Runnable() {
            @Override
            public void run() {
                objectLock();
            }
        }, "Thread2").start();
        System.out.println("Thread2" + "--start");
    }
​
    private void objectLock() {
        synchronized (lock) {
            System.out.println(Thread.currentThread().getName() + "--get lock");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "--release lock");
        }
    }

在锁的基础上,当线程拿到锁执行到一定阶段,需要让其他线程拿到锁进行一些前置操作,如初始化、同步信息等再继续执行时,可通过锁的wait方法挂起当前线程,并释放锁。待其他线程处理结束后通过锁的notify、notifyAll唤醒wait线程继续执行

输入:

Thread1--start Thread1--invoke doWork Thread1--get lock Thread1--wait Thread2--start Thread2--invoke prepare Thread2--get lock Thread2--notify Thread2--release lock Thread1--continue Thread1--release lock

可见线程1先拿到了锁,但却后结束。

private void testWaitNotify() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                doWork();
            }
        }, "Thread1").start();
        System.out.println("Thread1" + "--start");
​
        new Thread(new Runnable() {
            @Override
            public void run() {
                prepare();
            }
        }, "Thread2").start();
        System.out.println("Thread2" + "--start");
    }
​
    boolean isInit = false;
​
    private void doWork() {
        System.out.println(Thread.currentThread().getName() + "--invoke doWork");
        synchronized (lock) {
            System.out.println(Thread.currentThread().getName() + "--get lock");
            try {
                if (!isInit) {
                    System.out.println(Thread.currentThread().getName() + "--wait");
                    lock.wait();
                    System.out.println(Thread.currentThread().getName() + "--continue");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "--release lock");
        }
    }
​
    private void prepare() {
        System.out.println(Thread.currentThread().getName() + "--invoke prepare");
        synchronized (lock) {
            System.out.println(Thread.currentThread().getName() + "--get lock");
            try {
                Thread.sleep(200);
​
                System.out.println(Thread.currentThread().getName() + "--notify");
                lock.notify();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "--release lock");
        }
    }
join

正常情况下两个线程开始执行,其中Thread1有耗时操作,肯定是Thread2先执行完。

输出:

Thread1--start Thread1--doWork Thread2--start Thread2--stop Thread1--stop

private void testJoin(){
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName() + "--doWork");
                    Thread.sleep(200);
                    System.out.println(Thread.currentThread().getName() + "--stop");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "Thread1");
        t1.start();
        System.out.println("Thread1" + "--start");
​
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "--stop");
            }
        }, "Thread2");
        
        t2.start();
        System.out.println("Thread2" + "--start");
    }

当在Thread2的run方法中加入后,会先执行完Thread1才会执行Thread2的后续逻辑

输出:

Thread1--start Thread1--doWork Thread2--start Thread2--Thread1 join Thread1--stop Thread2--stop

Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName() + "--Thread1 join");
                    t1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "--stop");
            }
        }, "Thread2");
yield

它让掉当前线程 CPU 的时间片,使正在运行中的线程重新变成就绪状态,并重新竞争 CPU 的调度权, 注意:不释放锁

yield 和 sleep 的异同

1)yield, sleep 都能暂停当前线程,sleep 可以指定具体休眠的时间,而 yield 则依赖 CPU 的时间片划分。

2)yield, sleep 两个在暂停过程中,如已经持有锁,则都不会释放锁资源。

3)yield 不能被中断,而 sleep 则可以接受中断。

interrupt

线程的中断,两种方式

1.设置标记位,通过改变标记值结束线程

方式1较为简单,但需要在线程中的关键点判断标志位的值

2.调用线程的interrupt方法

可以响应interrupt的情况:线程处于sleep、wait状态

各方法调用对应的线程state

Thread.sleep(long millis) -> TIMED_WAITING

wait(long millis) -> TIMED_WAITING

wait() -> WAITING

join() -> WAITING

也就是说线程状态为WAITING/TIMED_WAITING的线程可通过interrupt中断并抛出InterruptedException

注意:当前线程被中断后会重置中断状态

if any thread has interrupted the current thread. The
<i>interrupted status</i> of the current thread is
cleared when this exception is thrown.
private void stopThread() {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!Thread.interrupted()) {
                    try {
                        System.out.println(Thread.currentThread().getName() + "--doWork");
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        System.out.println(Thread.currentThread().getName() + "--throw InterruptedException");
                        // 当sleep/wait被中断会抛出异常,并清除当前线程的中断状态,没有这行会一直执行
                        Thread.currentThread().interrupt();
                    }
                }
​
                System.out.println(Thread.currentThread().getName() + "--stop");
            }
        }, "Thread1");
        t1.start();
        System.out.println("Thread1" + "--start");
​
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(300);
                    t1.interrupt();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "--stop");
            }
        }, "Thread2");
        t2.start();
        System.out.println("Thread2" + "--start");
    }

Thread2在开始300ms后向Thread1发送中断信号,Thread1当时在TIMED_WAITING状态(sleep),抛出InterruptedException异常,被捕获后再次设置为中断状态,通过Thread.interrupted()判断后退出循环。

输出:

Thread1--start Thread1--doWork Thread2--start Thread2--stop Thread1--throw InterruptedException Thread1--stop

即通过interrupt停止线程有两种方式,

1.以InterruptedException的方式终止

2.线程中代码逻辑多环节判断Thread.interrupted(),为true则退出线程

异常

java.lang.IllegalMonitorStateException

 * Thrown to indicate that a thread has attempted to wait on an
 * object's monitor or to notify other threads waiting on an object's
 * monitor without owning the specified monitor.

当一个线程在没有成为object的monitor的owner时,也就是没在以object为锁的synchronized代码块内调用

该object的wait则会抛出此异常。