线程相关知识

一.对线程的理解:

 1. 线程是进程中的一个顺序执行流,在程序中表现为一个对象,Thread类;多个Thread类对象可以并发执行,线程内部的多个操作肯定是顺序执行的。

  2.进程:进程可以理解为正在运行的程序,多个进程可以并发执行,一个进程可以启动多个线程。

  3.线程并发执行:

     (1)如果计算机中只有一个cpu,那么系统每次只会执行一个线程,获得cpu时间片的线程会执行,随后其他线程抢占cpu,获得时间片继而执行其他线程,所谓并发其实质是在某个时间节点上还是一个线程被执行,并发效果体现在cpu在多个线程之中来回切换。

     (2)如果计算机中有多个cpu,那么系统会实现同时执行多个线程;

     (3)在微观体现上:多个线程在顺序执行;在宏观体现上:以肉眼观看,多个进程并发执行;

  4.多线程的应用场合:当我们需要处理多个任务:如下载多个文件:音乐,电影等;并发加载多张图片中可以考虑使用多线程;

  5.使用多线程的优势和劣势:

     ①优势:提高了执行效率,用户体验更流畅;

     ②劣势:数据的安全不好控制,业务的调试开发难度加大;

二.线程的创建启动及运行:

 1.java中线程对象是Thread类型的,线程的创建需要实例化Thread对象;Thread类的构造方法有Thread(){}和Thread(Runnable run){};构造方法中重写run()方法;  

  2.线程的启动需要调用Thread类的对象的start()方法;注意:启动不代表运行,而是等待分配cpu状态;

        new Thread(){            @Override            public void run() {            }        }.start();                        new Thread(new Runnable() {            @Override            public void run() {            }        }){        }.start();

     3.运行:当线程获得cpu时,会调用Thread对象的run()方法;注意!new Rundable(){public void run(){}}.start;中Rundable类的对象的run()方法底层调用的是所在线程的run()方法;

三.主线程和工作线程:

      1.主线程:java中运行在main方法中的线程称之为主线程;而安卓中运行在onCreate()方法中的线程为主线程也称之为UI线程;

      2.工作线程:在主线程之下的实例化的线程称之为工作线程;

      3.主线程与工作线程之间的协作:安卓中UI操作必须运行在主线程之中,而工作线程一般执行一些耗时操作比如下载,加载图片等;

四.线程注意问题:

         1.线程的运行存在不确定性,线程的运行自己不能控制而是有操作系统控制;何时分配cpu,何时执行该线程;

         2.方法运行在哪个线程取决于对哪个线程进行了调用;

      3.一个对象的不同方法可以运行在不同线程;

五.java中线程对象状态及相关方法:

      1.线程对象的状态:

           ①新建状态:new Thread(){}

           ②就绪状态:调用线程对象的start()方法;

           ③启动状态:获得cpu,执行线程对象的run()方法;

           ④阻塞状态:sleep(), 调用对象锁的wait()方法;注意!!一般不应让主线程处于阻塞状态而是选择工作线程让其等待;是为了更好地用户体验;

           ⑤死亡状态:run()方法运行结束,该线程处于死亡状态;

     2.线程相关方法:

        (1)start():启动线程;此时线程处于就绪状态,等待获得cpu执行run()方法;

        (2)run():线程运行时执行此方法,线程执行完run()方法以后就会死掉,如何让线程不死掉!!在run()方法中写一个死循环!!!!

          (3)sleep():让当前线程休眠同时在该时间段内让出cpu;

          (4)setName();给线程起名字,不起名字也会有个默认名字;

          (5)getName();获得线程名字;

          (6)isAlive();判断线程是否活着;

          (7)setPriority():设置线程优先级(1,5,10)优先级越大越先执行;

        (8)setDaemon();设置线程为守护线程,启动之前设置,当其他线程运行结束时,该线程也自行死亡,守护线程一般为服务线程;(垃圾回收线程是一个服务线程,当没有其他线程存在时,也就没有垃圾了,垃圾回收线程也就自行死亡);

                 (9)join():插入线程,调用此方法的线程会优先执行,例如:A线程正在执行run()方法,在A的run()方法中调用B线程的join()方法,此时A线程阻塞,B线程开始执行;

          (10)currentThread():静态方法,获得当前线程;

      3.问题:

          (1)线程对象创建以后可以调用run()方法启动吗?不可以!

          (2)线程对象启动以后,线程会立即执行run()方法吗?不确定,有可能被其他线程抢占了cpu,该线程就处于阻塞状态;

          (3)多个线程可以并发执行吗?可以

          (4)工作线程可以启动工作线程吗?可以!

          (5)多个线程可以共同执行一个任务吗?可以;

六.java中线程的同步(互斥与协作):

        1.线程的异步:多条线程并发执行各自的业务;

     2.线程同步:是多个线程并发执行时,在共享数据集上的互斥与协作;多条线程有次序执行各自的业务;如排队上公车等;

     3.互斥是为了保证数据的安全,协作是让多个线程在共享数据集上进行通讯,以确保业务的合理性;

     4.java中线程的互斥:共享数据集上存在多线程并发非原子操作,在其他线程执行时,其他线程不能进入,而是排队执行。

     5.以StringBuffer为例:

public synchronized StringBuffer append(Object obj) {
        if (obj == null) {
            appendNull();
        } else {
            append0(obj.toString());
        }
        return this;
    }

      synchronized关键字给方法加锁,多个线程并发访问该方法时,某个线程进入该方法后,必须在执行完该方法后,释放该方法锁,其他线程才能进入该方法;这样就保证了多个线程并发访问同一数据集的非原子方法时,不会产生数据逻辑错误;

     6.为什么要排队执行的形象说明:

     联系到生活中的案例,如商场的试衣间,如果不加锁,当一个人进入试衣间试衣服时,试衣服的动作不是一下子完成的,而是要经过脱衣服,换衣服的过程,在这个过程中如果还没有执行完整套动作,如刚脱完衣服,其他人就闯入了,这样就造成了不安全的问题,需要对试衣间;

    多个线程,(多个顾客要试衣服)同时访问一个数据集,(同时要进试衣间试衣服),要执行非原子操作时,(试衣服的动作不是一步完成的,而是要脱衣服,换衣 服等动作)为了保证数据集安全(为了保证试衣服时不被其他人打扰);需要对数据集加锁!!(需要对试衣间加锁!!!)

      7.加锁的方式:

    (1)使用同步代码块;(拿出方法中的一部分代码加锁(对象锁保证是唯一的锁))

   (2)使用同步方法;(对方法加锁)

     Public synchronized void method01(){}对象锁默认为this

     Public static synchronized void method02(){}静态方法对象锁默认为类名.class (类对象)

8.案例:

 

static int count;

static class CountTask01 implements Runnable{

//为什么会有重复的现象:枷锁:

@Override

public synchronized void run() {

String tname=Thread.currentThread().getName();//获得当前线程的名字;

int num=++count;

System.out.println(tname+" : "+num);

}

}

public static void main(String[] args) {

//创建任务对象:

CountTask01 task=new CountTask01();

//创建并启动多个线程:有没有数据安全问题;有么有共享数据集

for(int i=0;i<5;i++){

new Thread(task).start();

}

}
该案例中,在main方法这个主线程中又开辟了多个工作线程,这几个工作线程同时访问task这个对象调用start()方法,共享count这个数据集,并且执行的操作为非原子操作!所谓非原子操作为对该数据集的操作不是一步完成的而是多步完成!!!

问题主要体现在++count上,count=count+1;先取到count然后得到count+1的值,再把该值赋给count;这是一个非原子操作!!!

 

    9.线程安全与不安全的一些类:

①.StringBuffer和StringBuilder类:

StringBuffer是线程安全的,在该类中的很多方法都加了锁;所以相应的执行效率要低一些;

StringBuilder是非线程安全的,执行效率高

在线程不安全的环境下:所谓线程不安全是指:多个线程并发访问同一数据集的非原子操作;这种环境下使用StringBuffer;

在能确保线程安全的环境下,可以使用StringBuilder;

②Vector,HashTable是一个线程安全的集合,但是不推荐使用了,因为执行效率太低,锁的是整个哈希表;

ConcurrentHashMap底层对哈希表进行了分段加锁,锁的是试衣间;

Collections.synchronizedList(参数);的方法转换集合为线程安全的集合;也可以考虑使用java.util.concurrent包下的一些集合;例如:ConcurrentHashMap;

   10.典型案例:生产者消费者模型:

1)生产者线程对象:负责向容器放数据;

2)消费者线程对象:负责从容器取数据;

3)容器对象:存放数据;(需要考虑线程安全问题)

 

/**外界通过此方法向容器放数据,默认放在size(最后)位置*/

public synchronized void put(Object obj){

/**1.判断容器是否已满,满了则等待(生产者线程等待)

 * 2.没满则放数据*/

if(datas.length==size){

try {

this.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

datas[size]=obj;

//3.有效元素个数加1

size++;

//4.通知消费者线程取数据;

this.notify();

}//此方法存在设计缺陷;满了的情况怎么办;

/**外界通过此方法取数据*/

public synchronized  Object take(){

/**1.判定容器是否为空,空的则让消费者线程等待

 * 2.如果不为空则取数据(默认从第0个位置取)*/

if(size==0){

try {

this.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

Object obj=datas[0];

//3.移动元素:

System.arraycopy(datas, 1, datas, 0, size-1);

//5.有效元素个数减一;

size--;

//4.size位置元素设置为null;

datas[size]=null;

//6.通知生产者线程可以继续放数据;

this.notify();

return obj;

}

}

注意加锁的位置:对共享的数据集加锁;

七.安卓中线程消息模型:

1.概述:

  安卓中线程应用机制:

  1)所有的UI操作都在主线程中进行;

  2)所有的耗时操作,例如下载都在工作线程中进行;

  2.消息模型中用到的对象:

 

安卓中的消息模型是实现安卓中线程通讯的方式。这种方式的实现依托于如下几个对象:

1)消息对象;Message负责承载线程之间需要承载的数据;

2)MessageQueue(消息队列:负责存储多个消息队列)

3)Looper(迭代消息队列的迭代器:负责迭代消息队列)

4)Handler (处理器:发送和处理消息)

3.理解:

给哪个线程发消息,取决于Handler中所关联的Looper对象;在Handler()的构造方法中,传入Looper对象,默认是关联的主线程的Looper对象;

如果将Handler看做是一个邮局,那么Message就是要运送的包裹,MessageQueue消息队列就是所有包裹的载体,如邮车,Looper对象就是用来迭代消息队列中的内容的邮递员,要将消息发送给哪个线程取决于Looper对象,也就是邮递员是哪个邮局的。

handlerMessage()方法会在收到消息后进行回调,在这方法中做一些逻辑处理;

4.工作线程给主线程发消息:

在工作线程中构建消息对象,将消息封装到该消息中,然后调用sendMessage()方法将消息发送给主线程;

在主线程中构建handler对象处理并接收工作线程中发来的消息,重写sendMessage()方法,在该方法中做出相应的逻辑处理;

5.主线程给工作线程发消息:

需要构建工作线程的Looper对象,在工作线程中构建Handler对象,重写handlerMessage()方法在方法中作出相应的业务处理;

6.HandlerThread类:

封装了Looper对象的创建和销毁的过程,可以简化对looper对象的操作;Looper.looper()方法为内置的无限循环,不断的迭代消息队列中的消息内容;只有当looper.quite()方法执行时,该循环才会停止;

7.杀死进程:

工作线程由主线程启动,主线程运行在进程之中,杀死进程的同时,所在的主线程和工作线程都会终止;

具体方法:

Progress.killProgress(Progress.myPid());//myPid()方法为当前正在执行的进程;

八.安卓中的AsyncTask类:

1.概述:

1AsyncTask是安卓中的一个工具类,此类实现了对异步任务处理逻辑的封装,是对消息模型应用技巧的巧妙实现;简化消息模型逻辑的编写!

2)我们在使用该对象时,需要构建此类类型的子类对象,然后重写相关方法,这些方法有些运行在工作线程,有些运行在主线程;异步:有多个线程;

 

2.AsyncTask相关方法:

1execute:(调用此方法执行任务,需要排队执行)

 (2)executeOnExecutor:(调用此方法这些任务可以并发执行)

 (3)onPreExecute:(运行在主线程,在doInBackground:之前执行)

 (4)doInBackground:(工作线程,用于执行耗时操作)

 (5)onPostExecute:(主线程在doInBackground:之后执行)

 (6)publishProgress(发布进度,一般在工作线程调用)

(7)onprogressUpdate(主线程,更新进度)

当调用异步任务的executeXXX方法,启动任务执行时,execute方法中的实际参数值会传递给doInBackground方法;

相关泛型,决定类中属性类型,方法参数类型,方法返回值类型;

Ablstract class AsyncTask<Params,Progress,Result>();

(1)Params

(2)Progress

(3)Result

 

/**此方法运行在主线程,在doInBackground方法之前之前,用于初始化*/

@Override

protected void onPreExecute() {}

 

//此方法运行在工作线程;

@Override

protected File doInBackground(Integer... params) {}

 

/**此方法可以运行在主线程:可以在此方法中更新进度*/

@Override

protected void onProgressUpdate(Integer... values) {}

 

//此方法在doInBackground()方法结束后执行;运行在主线程;

@Override

protected void onPostExecute(File result) {}

九.线程池:

1.概述:

(1)何为线程池:

  ①内存中的一块区域

       ②允许存储多个线程对象;

       类似:整数池,字符串池,消息池.....

 (2)线程池应用的目的:

①允许线程池中的线程可以重复使用;

②提高系统的执行效率,较少对象的创建和销毁的过程;

 (3)线程池的应用场合:

 1>需要反复创建线程,并发执行任务的场合;

例如:

同时下载多个文件;

同时处理多个用户请求;

     (4)java中或安卓中线程池相关API?

1.Executor(线程池顶层接口):代表一个线程池对象;

  1>ExecutorService(接口)

  2>ScheduledExecutorService(接口)

  3>ThreadPoolExecutor(实现类)

  4>..........,

我们通常可以调用ExecutorServices, ScheduledExecutorService的实现类的类型对象创建线程;

2.Executors(工具类:创建一些简单的线程池对象)

1>创建一个只有一个线程的线程池;newSingle.....

2>创建一个有固定上限的线程池;newFixedThreadPool

3>创建一个没有固定上限的线程池;newCachedThreadPool

说明:也可以直接构建ThreadPoolExecutor的对象;

3.线程池对象创建以后我们可以调用线程池的execute,submit等相关方法执行任务;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值