黑马程序员-----java多线程总结*

来源:互联网 发布:董小飒淘宝赚多少钱 编辑:程序博客网 时间:2024/05/02 04:51

---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------



一、多线程

调用run方法的对象必须是Runnable的子类对象,

第一种:继承Thread类,因为Thread类也实现了Runnable接口

第二种:实现Runnable接口,因为Runnable接口中没有start方法,所以要把该接口类对象引用作为参数传递给Thread类的构造方法

1、线程控制着进程的执行

2、一个线程又称为一个控制单元

3、JVM启动的时候,会产生一个java.exe进程也之对应

4、java.exe进程在运行过程中至少有一个线程负责java程序的执行,该线程叫做主线程

5、JVM启动之后,java.exe进程启动会有两个线程执行,一个是主线程,一个是垃圾回收机制

为什么需要垃圾回收机制?

因为如果没有垃圾回收机制,那么当主函数里的垃圾对象堆积到一定程度后,程序会停下来对垃圾进行回收处理,这样给用户的感觉就是程序运行一段时间又停一段时间,然后又运行。


线程和进程和OS(操作系统)的关系?

1、线程存在与进程中,OS先创建了进程,然后再创建线程

2、线程是JVM调用底层操作系统的功能来创建的

总结:进程和线程都是由OS创建的,JVM只是起了调用OS的功能而已!


创建新执行线程有两种方法。

第一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。

步骤:

1、继承Thread类,创建Thread类的子类

2、该子类复写Thread类的run方法

复写run方法注意事项:因为Thread类中的run方法没有抛出异常,所以子类中的run方法如果出现异常能够内部处理不能够抛

3、创建子类对象(相当于创建了线程),调用start方法

注意:

start方法的作用:一是启动了线程;二是调用了run方法


为什么会交替打印?

因为CPU是单核,此时程序中存在两个控制单元/线程,CPU要进行快速的切换动作

为什么每次打印的结果都不一样?

因为线程在运行的过程中,是在抢夺CPU的执行权,谁抢到了执行权就运行该线程。

为什么要复写run方法?

为了将自定义代码存放在run方法中,让线程运行指定内容

为什么非要调用start方法?

因为只有当调用start方法后,start方法的本地native方法和start0方法才会调用操作系统的底层资源创建开启线程

调用start方法后会立马执行该线程吗?

不一定!因为CPU在某一个时刻只会执行一个线程,其余线程都处于临时堵塞状态,都在等待执行权


线程从运行到消亡的过程:

start()方法:当调用了start方法后,线程不一定马上执行,很大可能会跳转到临时堵塞状态,只有很小的几率跳到运行状态

sleep(时间)方法和wait()方法:当调用了这两个方法后,线程会跳转到冻结状态,并放弃CPU的执行权

notify()方法(或者是sleep方法的时间结束):当调用了该方法后,冻结状态的线程会被激活,很大可能会跳转到临时堵塞状态,只有很小的几率跳到运行状态

stop()方法(或者是run方法运行完):当掉用了该方法后,线程会由运行状态走到消亡状态


决定线程是临时堵塞状态还是运行状态的因素是:看该线程是否拥有CPU执行权,有就运行,没有就堵塞


 

是否具有CPU执行资格

是否具有CPU执行权

 运行状态【双有】

冻结状态【双无】

----

临时阻塞状态【一有一无】




第二种方法是声明实现 Runnable 接口的类。该类然后实现run方法。然后可以分配该类的实例,在创建 Thread时作为一个参数来传递并启动。


实现Runnable接口相对于继承Thread类来说,有如下的显著优势:
   1.适合多个相同代码的线程去处理同一个资源的情况
   2.可以避免由于java的单继承特性带来的局限
   3.增强了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的




多线程安全问题:
实现Runnable接口可以让多线程访问同一资源,但是当某一线程处理共享区数据到一半时执行权别另一线程抢走了,就可能导致数据错误,为了避免出现这种情况,java引进了解决办法,就是同步代码块。原理是当某个线程在操作共享区数据时,其他线程不能够操作,只能够等该线程操作完毕才可以操作共享数据

代码:synchronized(对象){共享代码块},其中对象可以为任意,注意:要有设计到共享数据的运算才放在synchronize块里


同步代码块和同步函数的区别:

1、同步代码块的锁是任意对象;同步函数的锁是this

开发一般都用同步代码块!



多线程通信:

多线程通信就是多个线程在操作同一个资源,例子:一个线程往仓库里存东西,另一个线程从仓库取东西

多线程出现问题原因:

1、不是同一个锁

2、没有同步两个或两个以上的线程


多线程通信存在的问题:

加入这时候的需求是:仓库里只能够保持一件东西,这样的话就不可以了

这时候用到等待唤醒机制!




多线程等待唤醒机制:

分析:虽然一个线程往仓库存东西的时候另一个线程不能够取,但是要保证仓库里只能有一件物品,就需要用到wait()、notify()、notify()方法了

思路:首先判断,仓库里有没有东西,如果没有,则一个线程往里面存东西,这个过程通过锁来判断。当存完东西以后改变锁的状态,并且唤醒另一个线程从仓库中取出东西

为什么要用while不用if来判断是否等待?

因为如果有两个线程都在等待,Allnotify()后,用if的话第一线程往里面存入一个东西,改变锁的状态,但是第二个线程不会去判断锁的状态,继续往里面存入一个东西,造成错误;而while语句的特点是被唤醒以后会先又去判断条件是否满足

为什么要用Allnotify方法?

因为如果当有很多线程时,用notify方法,会唤醒存入线程而不唤醒取出线程,这样的话造成线程全部等待

经典例子:

public class Produce_Consume {public static void main(String[] args) {Resource r = new Resource();Produce p = new Produce(r);   //为了保证r对象是同一个Consume c = new Consume(r);  //为了保证r对象是同一个Thread t1 = new Thread(p);Thread t2 = new Thread(p);Thread t3 = new Thread(c);Thread t4 = new Thread(c);t1.start();t2.start();t3.start();t4.start();}}//共享资源class Resource{private String name;  //商品名称privateint count=1;   //计数private boolean flag= false;//生产public synchronized void intPut(String name){while(flag){try{wait();}catch(Exception e){}}this.name = name;count++;System.out.println(Thread.currentThread().getName()+this.name+this.count);flag=true;this.notifyAll();}//消费public synchronized void OutPut(){while(!flag){try{wait();}catch(Exception e){}}System.out.println(Thread.currentThread().getName()+this.name+this.count+"...............");flag=false;this.notifyAll();}}//生产者class Produce implements Runnable{private Resource r;Produce(Resource r){this.r = r;}public void run(){while(true){//r.intPut("面包");}}}//消费者class Consume implements Runnable{private Resource r;Consume(Resource r){this.r = r;}public void run(){while(true){r.OutPut();}}}



JDK 1.5以后,Lock替代了synchronized,Condiation替代了监视器方法(其中await替代了wait,signal替代了notify,signalAll替代了notifyAll)

好处在于:如果notifyAll的话,会唤醒本方线程,太浪费资源,但是用signal的话可以选择性的唤醒对方线程

import java.util.*;public class Produce_Consume {public static void main(String[] args) {Resource r = new Resource();Produce p = new Produce(r); // 为了保证r对象是同一个Consume c = new Consume(r); // 为了保证r对象是同一个Thread t1 = new Thread(p);Thread t2 = new Thread(p);Thread t3 = new Thread(c);Thread t4 = new Thread(c);t1.start();t2.start();t3.start();t4.start();}}// 共享资源class Resource {private String name; // 商品名称private int count = 1; // 计数private boolean flag = false;private Lock lock = new ReentrantLock();// 创建生产者condition方法private Condition condition_pro = lock.newCondition();// 创建消费者condition方法private Condition condition_con = lock.newCondition();// 生产public void intPut(String name) throws InterruptedException {lock.lock();try {while (flag) {condition_pro.await();}this.name = name;count++;System.out.println(Thread.currentThread().getName() + this.name+ this.count);flag = true;condition_con.signal();} finally {lock.unlock();}}// 消费public void OutPut() throws InterruptedException {lock.lock();try {while (!flag) {condition_con.await();}System.out.println(Thread.currentThread().getName() + this.name+ this.count + "...............");flag = false;condition_pro.signal();} finally {lock.unlock();}}}// 生产者class Produce implements Runnable {private Resource r;Produce(Resource r) {this.r = r;}public void run() {while (true) {try {r.intPut("面包");} catch (Exception e) {}}}}// 消费者class Consume implements Runnable {private Resource r;Consume(Resource r) {this.r = r;}public void run() {while (true) {try {r.OutPut();} catch (Exception e) {}}}}




---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------详细请查看:http://edu.csdn.net

原创粉丝点击