在Android中,我们通常采用如下两个方法启动新的线程
private void startThread(){
//第一种
new Thread(){
@Override
public void run() {
//do something
}
}.start();
//第二种
new Thread(new Runnable() {
@Override
public void run() {
//do something
}
}).start();
}
两者的差别不大,实际上Thread是实现了Runnable接口的一个实现类,实际上执行的仍然是一个Runnable。
线程的4个方法
函数名 | 作用 |
---|---|
wait() | 线程进入等待池中,同时失去对象的机锁,使其它线程可以访问。用户可以使用notify,notifyAll或者指定睡眠时间来唤醒当前线程 注意:wait,notify,notifyAll必须放在synchronized block中,否则会抛异常 |
sleep() | Thread的静态函数,作用是进入睡眠状态。它不能改变对象的机锁,即使睡眠了也持有对象锁。 |
join() | 等待目标线程执行完成之后再继续执行 |
yield() | 线程礼让。目标线程由运行状态转换为就绪状态,即让出执行权限,让其他线程得以优先执行,但其它线程是否优先执行是未知的 |
wait和notifyAll的运用示例:
public class Test {
private static Object sLockObject=new Object();
static void waitAndNotifyAll(){
System.out.println("主线程运行");
Thread thread=new WaitThread();
thread.start();
long startTime=System.currentTimeMillis();
try {
synchronized (sLockObject){
System.out.println("线程等待");
sLockObject.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
long time=System.currentTimeMillis()-startTime;
System.out.println("耗时:"+time+" ms");
}
static class WaitThread extends Thread{
@Override
public void run() {
System.out.println("waitThread 开始");
try {
synchronized (sLockObject){
Thread.sleep(3000);
sLockObject.notifyAll();
}
}catch (Exception e){
}
}
}
public static void main(String[] args) {
waitAndNotifyAll();
}
}
输出结果:
主线程运行
线程等待
waitThread 开始
wait让主线程执行等待,释放机锁后WaitThread得以运行,WaitThread中调用notifyAll后,主线程继续执行。
join阻塞当前调用join函数的线程,直到执行完毕后继续。
示例:
public class Join {
public static void main(String[] args) {
Worker worker1=new Worker("worker1");
worker1.start();
System.out.println("work1 start");
try {
worker1.join();
Worker worker2=new Worker("worker2");
worker2.start();
System.out.println("work2 start");
worker2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class Worker extends Thread{
public Worker(String name){
super(name);
}
@Override
public void run(){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("work in "+getName());
}
}
}
输出信息
work1 start
work in worker1
work2 start
work in worker2
可以看出join会阻塞主线程。
yield会主动让出线程控制权
public class Yield {
public static void main(String[] args) {
YieldThread y1=new YieldThread("yield-1");
YieldThread y2=new YieldThread("yield-2");
y1.start();
y2.start();
}
static class YieldThread extends Thread{
public YieldThread(String name){
super(name);
}
@Override
public synchronized void run(){
for(int i=0;i<5;i++){
System.out.printf("%s, priority %d --->%d\n",this.getName(),this.getPriority(),i);
if(i==2)Thread.yield();
}
}
}
}
输出结果
yield-2, priority 5 --->0
yield-1, priority 5 --->0
yield-2, priority 5 --->1
yield-2, priority 5 --->2
yield-1, priority 5 --->1
yield-1, priority 5 --->2
yield-2, priority 5 --->3
yield-1, priority 5 --->3
yield-1, priority 5 --->4
yield-2, priority 5 --->4
与多线程相关方法
- Runnable 既能运行在Thread中,也能运行在线程池中。其它几个只能运行在线程池中。
- Callable 一个泛型接口,有一个返回值
public interface Callable<V>{
V call() throws Exception;
}
- Future 为线程池制定了一个可管理的任务标准。提供对Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果、设置结果操作,分别对应cancel、isDonw、get、set函数。get方法会阻塞,直到任务返回结果。
- FutureTask 对Future的实现类。FutureTask实现了RunnableFuture<V>,而RunnableFuture<V>实现了Runnable、Futrue<V>两个接口。
Runnable、Callable、FutureTask示例
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class FutureDemo {
static ExecutorService mExecutor=Executors.newSingleThreadExecutor();
public static void main(String[] args) {
try {
futureWithRunnable();
futureWithCallable();
futureTask();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
private static void futureWithRunnable() throws InterruptedException, ExecutionException{
Future<?> result = mExecutor.submit(new Runnable(){
@Override
public void run() {
fibc(20);
}
});
System.out.println("future result from runnable:"+result.get());
}
private static void futureWithCallable() throws InterruptedException, ExecutionException{
Future<Integer> result = mExecutor.submit(new Callable<Integer>(){
@Override
public Integer call() throws Exception {
return fibc(20);
}});
System.out.println("future result from callable:"+result.get());
}
private static void futureTask() throws InterruptedException, ExecutionException{
FutureTask<Integer> futureTask = new FutureTask<Integer>(new Callable<Integer>(){
@Override
public Integer call() throws Exception {
return fibc(20);
}});
mExecutor.submit(futureTask);
System.out.println("future result from futureTask:"+futureTask.get());
}
private static int fibc(int num){
if(num==0)return 0;
if(num==1)return 1;
return fibc(num-1)+fibc(num-2);
}
}
执行结果
future result from runnable:null
future result from callable:6765
future result from futureTask:6765
线程池
通过线程池的统一调度、管理使得多线程的使用更简单、高效。
线程池都实现了ExecutorService接口,它的实现有ThreadPoolExecutor和ScheduledThreadPoolExecutor。
通常我们不会用new的形式创建线程池,而是用Executors工厂类来生成。
四大线程池:
- fixThread 正规线程池,定长线程池,超出会等待,无超时,队列大小不限,关闭才会回收
- CacheTheadPool 缓存线程池,线程数大,为每个任务任务添加新线程,60秒没用到就回收。
- SingleThreadPool 单线程池,里面的线程要排队等待,不处理并发,不会被回收。
- ScheduledThreadPool 唯一有延迟执行和周期执行的线程池。核心线程池固定,非核心线程没有限制,但闲时立即回收。
同步集合
- CopyOnWriteArrayList 多线程共享同一列表的并发容器,另外并发容器还有CopyOnWriteArraySet
- ConcurrentHashMap HashTable是HashMap的线程安全实现,但它使用synchronized来保证线程安全,效率低。ConcurrentHashMap采用锁分段技术提升效率。
- BlockingQueue
函数名 | 作用 |
---|---|
add(e) | 把元素加入到BlockingQueue,可以容纳则返回true,否则抛出异常 |
offer(e) | 将元素加到BlockingQueue,可以容纳则返回true,否则返回false |
offer(e,time,unit) | 将元素加到BlockingQueue,可以容纳则返回true,否则再等待指定的时间后再尝试添加,失败则返回false |
put(e) | 将元素加到BlockingQueue,如果不能容纳则会阻塞线程直到里面可以继续添加 |
take() | 取走排在队首的对象,若队列为空则进入等待状态直到有新对象加入为止 |
poll(time,uint) | 取出并移除队首元素,设定的时间内没有获取到则返回null |
element() | 获取队首元素,如果队列为空则抛出NoSuchElementException异常 |
peek() | 获取队首元素,队列为空则返回 null |
remove() | 获取并移除队首元素,队列为空则抛出NoSuchElementException异常 |
BlockingQueue
在Android的实现有:
ArrayBlockingQueue
数组实现的、纯种安全的、有界的阻塞队列。按先进先出原则进行排序,从尾部插入,从头部开始返回
LinkedBlockingQueueb
单向链表的阻塞队列。按先进先出原则进行排序,从尾部插入,从头部开始返回
LinkedBlockingDeque
双向链表实现的双向并发阻塞队列。同时支持先进先出和先进后出两种操作,可以从队列的头和尾同进操作(插入/删除)。
同步锁
- synchronized 能作用于对象、函数、class。每个对象都只有一个锁,谁拿到谁有访问权,当synchronized作用于函数时,实际上也锁的也是对象,即函数据在的对象。
- 显示锁--ReentrantLock与Condition
ReentrantLock和内置锁synchronized实现了相同的语义,但具有更高的灵活性。显示锁将锁的获取和释放分开,同时可以提供轮训锁和定时锁,同时可以提供公平锁或非公开锁。
基本操作
函数 | 作用 |
---|---|
lock() | 获取锁 |
tryLock() | 尝试获取锁 |
tryLock(longtimeout,timeUnit) | 尝试获取锁,如果指定时间还获取不到那么超时 |
unlock() | 释放锁 |
newCondition() | 获取锁的Condition |
使用ReentrantLock的一般Lock、tryLock与unlock成对出现。
Lock lock = new ReentrantLock();
public void dosomething(){
lock.lock();
try{
} finally {
// unlock必须在finally中调用,防止catch
lock.unlock();
}
}
Condition
函数 | 作用 |
---|---|
await() | 线程等待 |
await(int time,TimeUnit) | 线程等待指定时间,超过时间则为超时 |
signal() | 随机唤醒某个等待线程 |
signalAll() | 唤醒所有等待中的线程 |
- 信号量Semaphore
Semaphore是一个计数信号量,它的本质是一个“共享锁”。它可以指定同时持有许可的个数,通过acquire
获取许可,通过release
释放许可,超过许可则等待。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SemaphoreDemo {
public static void main(String[] args) {
final ExecutorService executorService=Executors.newFixedThreadPool(3);
final Semaphore semaphore=new Semaphore(3);
for(int i=0;i<5;i++){
executorService.submit(new Runnable(){
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("剩余许可:"+semaphore.availablePermits());
Thread.sleep(2000);
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}});
}
}
}
运行结果:
剩余许可:1
剩余许可:1
剩余许可:0
剩余许可:2
剩余许可:1
第一次输出前3条,2秒后输出后面两条。
循环栅栏CyclicBarrier
一个同步辅助类,允许一组线程互相等待,直到达到公共屏障点。通过示例理解:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
private static final int SIZE = 5;
private static CyclicBarrier mCyclicBarrier;
public static void main(String[] args) {
mCyclicBarrier = new CyclicBarrier(SIZE, new Runnable(){
@Override
public void run() {
System.out.println("-->满足条件,执行操作。参与者:"+mCyclicBarrier.getParties());
}});
//
for(int i=0;i<SIZE;i++){
new WorkThread().start();
}
}
static class WorkThread extends Thread{
public void run(){
try {
System.out.println(Thread.currentThread().getName()+" 等待CyclicBarrier.");
mCyclicBarrier.await();
System.out.println(Thread.currentThread().getName()+"继续");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
执行结果:
Thread-0 等待CyclicBarrier.
Thread-3 等待CyclicBarrier.
Thread-2 等待CyclicBarrier.
Thread-1 等待CyclicBarrier.
Thread-4 等待CyclicBarrier.
-->满足条件,执行操作。参与者:5
Thread-4继续
Thread-0继续
Thread-1继续
Thread-3继续
Thread-2继续
从示例可以看出,当await的个数达到5后,会先调用CyclicBarrier里的Runnable,然后5个线程会一起继续执行。相当于5个一组,5个一组的等待执行。
闭锁CountDownLatch
也是一个同步辅助类,在完成一组正在其它线程中执行的操作之前,它允许一个或多个线程一直等待,直到条件被满足。
代码示例:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
private static final int LATCH_SIZE = 5;
public static void main(String[] args) {
try {
CountDownLatch latch=new CountDownLatch(LATCH_SIZE);
for(int i=0;i<LATCH_SIZE;i++){
new WorkerThread(latch).start();
}
System.out.println("主线程等待");
latch.await();
System.out.println("主线程继续执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class WorkerThread extends Thread{
CountDownLatch mLatch;
public WorkerThread(CountDownLatch latch){
mLatch=latch;
}
public void run(){
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"执行操作");
mLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
主线程等待
Thread-3执行操作
Thread-4执行操作
Thread-1执行操作
Thread-2执行操作
Thread-0执行操作
主线程继续执行
AsyncTask
它对Thread+Handler的良好封装,减少了开发者处理问题的复杂度,提高了开发效率
- AsyncTask对象必须在主线程中创建
- AsyncTask对象的execute方法必须在主线程中调用
- 不要在程序中直接调用
onPreExeute()
、onPosExeute
、doInBackground
和onPressessUpdate
方法 - 一个AsyncTask对象只能调用一次execute方法
//调用task
for (int i =0;i<6;i++){
new MyTask().execute("task"+i);
}
class MyTask extends AsyncTask<String,Integer,String>{
@Override
protected String doInBackground(String... strings) {
String name=strings[0];
System.out.println(name+" start1");
SystemClock.sleep(1000);
System.out.println(name+" start2");
SystemClock.sleep(1000);
System.out.println(name+" end");
return "s";
}
}
09-05 10:53:48.014 25644-25974/com.yy.itemtouchhelper I/System.out: task0 start2
09-05 10:53:49.049 25644-25974/com.yy.itemtouchhelper I/System.out: task0 end
09-05 10:53:49.052 25644-26064/com.yy.itemtouchhelper I/System.out: task1 start1
09-05 10:53:50.086 25644-26064/com.yy.itemtouchhelper I/System.out: task1 start2
09-05 10:53:51.126 25644-26064/com.yy.itemtouchhelper I/System.out: task1 end
09-05 10:53:51.128 25644-26120/com.yy.itemtouchhelper I/System.out: task2 start1
09-05 10:53:52.148 25644-26120/com.yy.itemtouchhelper I/System.out: task2 start2
09-05 10:53:53.173 25644-26120/com.yy.itemtouchhelper I/System.out: task2 end
09-05 10:53:53.174 25644-26120/com.yy.itemtouchhelper I/System.out: task3 start1
09-05 10:53:54.210 25644-26120/com.yy.itemtouchhelper I/System.out: task3 start2
09-05 10:53:55.214 25644-26120/com.yy.itemtouchhelper I/System.out: task3 end
可以看出,AsyncTask是采用一个线程来串行执行任务的,如果要并行执行任务,可以通过调用AsyncTask的executeOnExecutor来实现。
网友评论