黑马程序员————Java基础日常笔记---对多线程的理解

来源:互联网 发布:笔记本安装linux 编辑:程序博客网 时间:2024/06/05 05:16

<span style="font-family: SimSun; background-color: rgb(255, 255, 255);"> ------</span><a target=_blank href="http://www.itheima.com" target="blank" style="font-family: SimSun; background-color: rgb(255, 255, 255);">Java培训、Android培训、iOS培训、.Net培训</a><span style="font-family: SimSun; background-color: rgb(255, 255, 255);">、期待与您交流!</span>

1,线程的由来:

1.1:进程与线程的理解:
进程是一个执行中的程序,
每一个进程执行都有一个执行的顺序,该顺序是一个执行路径,或者叫一个控制单元;
进程:
应用程序只要是启动,都会在内存中分配一片空间(地址),进程就是用来标识这个空间的,
用于封装里面代码的控制单元。
因此:线程才是进程中的真正执行的部分;
也就是说一个进程至少有一个线程(控制单元)。
如果这个线程运行的代码存在于main方法中,那么这个线程叫主线程。
主线程不是单线程,当JVM启动时, 就是一个多线程,
原因:
在执行主函数是, 执行的是主线程, 但是在内存中, 栈堆中会建立很多的对象, 如果对象不被使用, 会垃圾回收,主线程还在执行下面的操作, 但是被垃圾回收的对象就被干掉了,这就是同时在进行,  
主线程在继续执行的同时 , 这个垃圾被垃圾回收了,所以JVM这时至少有两个线程
一个是主线程, 
一个是负责垃圾回收的线程

1.2 多线程存在的意义

多个线程的出现,可以让一个程序中的多个代码同时执行, 这样就可以提高程序运行的效率;
比如:
如果就是一个主线程,
那内存中产生的垃圾没有回收,那么就有很多的垃圾,直到内存放不下了, 就会出问题, 
这时JVM就会停下来, 即主线程先停在这个位置,先要把垃圾处理,然后在执行,这样就不合理了, 
但是如果这个主线程一直逐条执行,而另一个线程在处理垃圾,有两个控制单元(执行路径),这样就会效率更快;

2 线程的创建:

多线程的创建就是为了让某些代码能够同时执行,

那如何才能在自定义的代码中自定义控制单元呢?

首先, 创建这个控制单元, 就是执行路径,他本身也是一类事物,因此就可以把它描述成一个类了;
然后创建这个控制单元的类的对象, 这样就可以调用这个类的方法(功能),来创建多条执行路径,实现代码的同步执行。
这执行路径是在进程中的, 进程是系统Windows所创建的, 因此进程中的线程也是windows帮忙创建的,而jvm依赖于系统,只需要调用系统中的内容,既可以完成动作。
而java提供了对线程对象的体现,被java虚拟机封装成了对象,因此可以直接调用。
就需要找对象, 在API中查找,java就已经提供了对线程这类事物的描述,就是Thread类。


java虚拟机允许应用程序并发地允许多个执行线程。

2.1 创建线程方法一:继承Thread类

这种方法是将类声明为Thread的子类, 该子类应重写Thread类的run()方法。

通过对API的查找, java已经提供了对线程这类事物的描述,就是Thread类。

代码如下

class Demo extends Thread//继承Thread对象,让他成为Thread类的子类,这样就可以使用Thread类中的方法了{public void run(){//将需要执行的代码放在这里for(int i=0;i<68;i++){System.out.println("Demo run.."+i);}}}class ThreadDemo_1 {public static void main(String[] args) {Demo d=new Demo();//创建了一个线程//d.run();//如果这样的话,就是调用demo的run()方法, 只有一个主线程d.start();//调用这个方法, 就是创建这个线程, 并启动了这个线程,让线程开始执行,JVM就会调用这个线程的run()方法了for(int i=0;i<68;i++){System.out.println("main..."+i);}}} 

2.1.1 继承thread类创建线程——分析

Jvm创建了主线程,

到了d=newDemo();,就创建了新的控制单元,线程被创建了,被主线程所开启的   那么这个程序就多了一个执行路径。


这里需要注意:
这两个线程在CPU不会同时执行的,Cpu在某一个时刻只执行一个执行路径,那么CPU在这些程序之间做着快速的切换,速度很快,所以你会感觉同时执行,
所以CPU 在切换进程, 也就是在切换每个线程。
多个线程在争抢CPU的资源,真正的是CPU 在执行,一个意思;
当主线程完了, 但是d还没有完, 搞定后,才停止。
但是这个程序的运行结果每次的都不同,为啥?
原因:
因为多个线程都在获取cpu 的执行权, cpu执行到谁, 谁就运行,明确一点, 在某一个时刻, 只能有一个程序在运行,(多核除外)
我们可以形象把多线程的运行行为在互相抢夺Cpu的执行权
这就是多线程的一个特性: 随机性 谁抢到谁执行,至于执行多长,cpu说的算
这样Cpu就优化资源。
为什么要覆盖run方法呢?
Thread类用于描述线程
该类就定义了一个功能, 用于存储线程要运行的代码 该存储功能就是run方法;
同样,主线程要运行的代码在main方法中, 这是JVM定义,存放在main方法中;
如果直接在主线中run()方法
可以直接调用run ,但是在run()方法中没有任何东西;
开启线程的目的是为了运行指定的代码,父类提供了空间,你有代码的话,怎么做才能被运行到呢?
复写run(),并将需要运行的代码放在run() 这样就可以运行到了;
编译的时候, 运行的是start()调用的是父类的run()方法, 但是子类把父类的run()方法给覆盖了,所以调用的是子类的run()方法,
从而就继承thread的类并复写run()方法了。
总结:继承Thread类创建线程:流程:
1,定义类来继承Thread;
2,复写Thread类中的run()方法,
             目的:将自定义的代码存储在run()方法中, 让线程运行。
3,主函数中创建Thread子类的对象, 然后调用这个线程的start()方法,
             该方法的作用:a: 启动线程
                          b:调用run()方法

2.2 线程的运行状态
没有执行资格的情况下是冻结状态;
有执行资格的情况下是临时状态;
既有执行资格又有执行权, 是运行状态;
程序结束了,就是消亡状态;
图解:

细节:


2.3 创建线程方法二——实现Runnable接口
问题来源:
如果仍然继承Extends类,
如果一个程序中有多个线程同时需要共享数据, 
共享数据可以把他定义成静态,但是这样,他的生命周期会很长,不建议;
因此可以定义一个线程,并让这个线程都去开启执行start(),然而这样做是没有意义的, 
就会提示线程状态出错,因为已经运行的程序不需要再次开启的,所以继承Extends类就不太靠谱了,
 
所以,采用创建线程的第二种方法, 声明实现Runnable接口的类, 
原理:
Runnable接口应该由那些打算通过某一线程执行这个实现Runnable接口的类的实例来实现
他为非Thread子类的类(执行这个实现Runnable接口的)提供了一种激活方式。
Runnable接口中的抽象方法只有一个run(),
这个run()的作用:将需要执行另一条路径的代码(明确要运行什么代码)存储在这里, 
然后实现Runnable接口的类的对象,创建一个线程启动该线程将导致在独立执行的线程中执行这个对象的run方法,
如何来创建这个线程呢?
通过创建Thread类的对象,来调用这个有参的构造方法  thread的构造函数中,就有这个那么就在new thread对象的同时, 就指定run方法所属对象 ,这样就ok了。

实现Runnable类来创建线程,原因在上面已经分析清楚,这里总结下实现Runnable类来创建线程的过程:
1,定义类实现Runnable接口;
2,覆盖Runnable接口中的run()方法
        将线程要运行的代码存放在该run方法中, 
3,通过Thread类建立线程对象;
4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造方法中, 
      为什么要将Runnable接口的子类对象传递给Thread的构造方法。
      因为,自定义的run()方法所属的对象是Runnable接口的子类对象, 所以要让线程去指定对象的run()方法, 
      就必须明确该run()方法所属的对象
5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
2.3.1 创建线程——实现方式和继承方式的区别
个人理解:
继承Thread类, 比较方便, 但是有局限, 
他的子类只能继承单一的线程,有局限性, 
所以在定义线程时, 建议使用实现的方式,
因为一个类可以多实现, 可以作为实际参数传递到Thread类的构造方法中, 
区别:
继承Thread:线程代码存放在Thread子类run()中;
实现Runnable:线程代码存放在接口的子类的run()中;
2.4线程中的安全问题和解决方案:
线程中的安全问题
安全问题的由来:

当一个程序中有多个线程同时运行的时候, 并且是共享同一数据资源,
那么多个线程会争取抢夺CPU的执行权,当其中的一个线程使用了这个共享数据,但是只是执行了一部分, 还有一部分的
代码没有执行完, 另一个线程就争抢到了CPU的执行权, 同样执行那这一部分,从而导致共享数据的错误。

解决方案:
对多个线程共享数据的语句只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行,即使其他线程有了CPU的执行
权了无法进来执行, 这就需要一个门来限制,叫做同步代码块;
synchronized(对象)
{
需要被同步的代码     //哪些代码需要同步, 就要看那些代码在操作共享数据,
}

当然门上需要有锁来标识的和控制的,这里的“对象”,就相当于这把门上的锁, 也叫监视器。
这个锁是如何操作的呢?
当然在第一个线程进来的时候, 这个锁当然是开着的, 当这个线程进来之后, 就会把这个门关上,程序中就是同步代码块。
通过同步代码块中的对象来标识,
对象如同锁, 持有锁的线程可以在同步中执行,没有持有锁的线程即使获取了cpu的执行权了,也进不去, 因为没有获取锁,
因此同步就可以解决问题了。
2.4.1 线程的同步的前提——重点
1, 必须要有两个或者两个以上的线程有共享数据的操作
2,必须是多个线程使用同一个锁。
必须保证同步中只有一个线程在运行, 
有些代码需要同步, 有些代码不需要同步, (看是否在共享数据,因为安全问题是由于共享数据所产生的)
同步的好处:
解决了多线程的安全问题
弊端:
多个线程需要判断锁, 较为消耗资源。

2.4.2 线程——同步代码块和同步函数
当一个函数中的代码都共享的数据,并且不会出现异常, 那么就可以把这个函数同步。 
原因:
同步代码块用来封装需要同步的共享数据的代码。
函数:也是用来封装代码的, 如果这个函数中的代码都是共享的数据时, 那么就可以将这个函数同步, 

2.4.3 线程——同步函数,静态函数
首先,看程序中是否有安全问题, 是否需要同步?如何找问题:
1,明确哪些代码是多线程运行的代码;
2,明确共享数据
3,明确多线程运行代码中哪些语句是操作共享数据的。
同步函数的锁是this,
静态函数的锁是class字节码对象。

2.5 线程—单例设计模式下的安全问题

终身指向newSingle(),所以不管线程多少, 都不会产生安全问题。


单例设计模式下懒汉式的作用:延迟加载,

问题的产生:

当有多个线程执行到if语句时, 
这时被其他的线程抢夺了CPU, 那么这样的话, 就会产生多个对象, 这样就会产生安全隐患。
因为单例设计中,只能有一个该类对象被创建。

那如何解决呢?
这里就需要同步, 前提: 多个线程在操作共享资源。

 

2.png (20.11 KB, 下载次数: 0)

下载附件  保存到相册

昨天 08:32 上传


这样就可以解决, 
但是这样的话, 每次都要先判断这个锁,这样会比较低效,
因此:在锁的上面,再加一个判断,

 

3.png (14.26 KB, 下载次数: 0)

下载附件  保存到相册

昨天 08:34 上传


这样双重判断,减少了锁的判断次数, 就会提高效率了,

2.6 线程——死锁问题:

在同步中, 有时会产生死锁,这样我们需要尽量避免死锁的发生,
什么是死锁呢?——同步里面嵌套同步;

你持有一个锁,我也有一个锁, 我要到你那里去运行,我要拿你的锁,但是谁都不放锁, 就导致程序死锁了,程序死锁, 程序就挂着不动了,
如何产生死锁呢?
代码如下:
class Test implements Runnable{private boolean flag;Test(boolean flag){this.flag=flag;}public void run(){if(flag){while(true){synchronized(Mylock.locka){//同步中嵌套同步System.out.println("a锁说:这是我的锁");synchronized(Mylock.lockb){System.out.println("a锁说:谢谢你的b锁");}}}}else{while(true){synchronized(Mylock.lockb){System.out.println("b锁说:这是我的锁");synchronized(Mylock.locka){System.out.println("b锁说:谢谢你的a锁");}}}}}}//为了方便class Mylock{static Object locka=new Object();//静态的目的为了方便调用static Object lockb=new Object();}class ThreadDeadTest {public static void main(String[] args) {Thread t1= new Thread(new Test(true));Thread t2= new Thread(new Test(false));t1.start();t2.start();}}
运行结果:
 
从代码分析, 开始线程二抢到CPU的执行权, b锁向a锁要到了锁, 所以就
 出现了和谐的状态;
但是之后, 就出现了死锁。  ,谁都不给谁, 就僵持着,哎!!!
2.7 多线程间的通信:
多线程间的通信:
就是多个线程在操作同一个资源,但是操作的动作不同。
如果不同步, 那么线程之间在抢夺CPU的执行权的时候, 就会出现数据的错误。
所以前提是在需要数据共享的代码中同步。
由于线程之间的功能不同, 所以即使写了synchronized, 也无法保证是值是正确的, 因此需要考虑同步中的另一个
前提,是否是同一个锁,而锁是一个任意的对象,由于他们是共享同一个资源, 所以这个锁可以是这个资源描述的类的对象;
2.7.1 等待唤醒机制
多线程——等待唤醒机制接着线程之间的通信, 这里有个问题, 同步了, 但是运行的话, 会执行一大片, 但是一般都是存一个,输出一个,
为什么会执行一大片?
一个线程可能一直拥有CPU的执行权;
如何解决?(参考代码)
这里可以用一个标记来标识(flag),当存一个完了之后, 就改变这个标识,当下次再执行时, 就不会再存了,
此时可以将这个线程1给冻结(sleep(时间(毫秒)),wait()),如果用sleep(),时间不确定, 所以就用wait(),当执行完了之后,
标识发生改变,再次执行时, 就处于冻结状态了,失去执行资格。
那什么时候恢复执行资格呢?
当另一个线程2进来将这个存的值,取走了, 就恢复执行资格,如何恢复?
当取走后, 标识再次发生改变,然后用notify(),唤醒线程1,此时如果这个线程2再次执行时, 由于没有了值, 那么就将线程2冻结,wait()。
这个过程就是线程间的等待唤醒机制。

代码如下:
<pre name="code" class="java">class Res //共享资源的类{String name;String sex;boolean flag=false;}/*等待的线程在哪里呢?线程运行的时候, 内存中会建立一个叫做线程池的,等待线程都存放在线程池中, 当notify的时候, 都是唤醒的是线程池中的线程,当线程池中有许多的线程都在等待,通常是唤醒第一被等待的线程, 按照顺序来叫醒*/class Input implements Runnable//需要用到一个资源,{private Res r;Input(Res r){//传递过来一个资源类的对象this.r=r;}public void run(){int x=0;while(true){synchronized(r)//这里需要同步,{if(r.flag){try{r.wait();}catch(Exception e){}//当为真时, 就等待, 不能再传值了,因为已经有值}if(x==0){r.name="mike";r.sex="man";}else{r.name="小王";r.sex="女女女女女女";}x=(x+1)%2;r.flag=true;r.notify();//在线程池中叫醒Output线程}}}}class Output implements Runnable{private Res r;Output(Res r){this.r=r;}public void run(){while(true){synchronized(r){//这里也需要同步,并且锁需要是同一个锁。if(!r.flag){try{r.wait();}catch(Exception e){}}//Wait(), notify(), notifyAll(), 全部用在同步中,因为需要锁必须标识wait()线程所处的锁System.out.println(r.name+"....."+r.sex);r.flag=false;r.notify();}}}}class ThreadInputOutput2{public static void main(String[] args) {Res r=new Res();Input in=new Input(r);Output out=new Output(r);Thread t1=new Thread(in);Thread t2=new Thread(out);t1.start();t2.start();}}

2.7.2 多线程——线程池
当线程进入冻结状态是, 这个等待的线程存放在哪里呢?
线程运行的时候, 内存中会建立一个叫做线程池的,等待线程都存放在线程池中, 当notify的时候, 都是唤醒的是线程池中的线程,当线程池中有许多的线程都在等待,通常是唤醒第一被等待的线程,按照顺序来叫醒,
notify()只能唤醒线程池中的第一个等待的线程,如果需要全部唤醒的话,就需要notifyAll();
唤醒线程池中所有等待的线程(不管是本方的线程,还是另一个线程);
说明:想wait(),notify(),notifyAll(),这些都不是Thread类中的方法,而是继承来自Object类中的方法,
定义线程的,怎么定义在Object类中呢?
这里需要调用这些方法, 该线程必须拥有此对象的监视器(锁),而同步才有锁, 而且是同一个对象的锁,
而两个对象的锁是不一样的, 因此需要来标识是哪个对象的锁。
而锁是任意的对象,所以可以被任意对象调用的方法(wait(),notify(),notifyAll()),就定义在objec类中了。
2.7.3 多线程的通信-生产者消费者

生产一个消费一个,

代码如下:

<pre name="code" class="java">class Resource{private String name;private int count=1;private boolean flag=false;public synchronized void set(String name){if(flag){try{this.wait();}catch(Exception e){}}this.name=name+"....."+count++;System.out.println(Thread.currentThread().getName()+"....生产...."+this.name);flag=true;this.notify();}public synchronized void out(){if(!flag){try{this.wait();}catch(Exception e){}}System.out.println(Thread.currentThread().getName()+"........消费........"+this.name);flag=false;this.notify();}}class Producer implements Runnable{private Resource res;Producer(Resource res){this.res=res;}public void run(){while(true){res.set("+商品+");}}}class Customer implements Runnable{private Resource res;Customer(Resource res){this.res=res;}public void run(){while(true){res.out();}}}class  ThreadProCus{public static void main(String[] args) {Resource res=new Resource();Producer pro=new Producer(res);Customer cus=new Customer(res);Thread t1=new Thread(pro);Thread t2=new Thread(cus);t1.start();t2.start();}}

结果如下:


这里有两个线程, 但是实际开发有多个线程来生产消费;

但是一般来说, 实际开发是有多个线程来生产消费的, 
 运行的话,就会有问题,
 。这样就不对了, 为啥?
原因:线程可能唤醒的是本方的线程, 而将前一个给覆盖了, 这时就需要将这个线程再次判断一下, 所以需要把if变成while循环。
而这样, 可能会将这些线程都给冻结了, 因此需要将notify(),改成notifyAll().

代码如下;

class Resource{private String name;private int count=1;private boolean flag=false;public synchronized void set(String name){while(flag){//用if判断, 数据错误, while循环,全部等待, 不是活着的, 冻结了try{this.wait();}catch(Exception e){}}this.name=name+"....."+count++;System.out.println(Thread.currentThread().getName()+"....生产...."+this.name);flag=true;this.notifyAll();}public synchronized void out(){while(!flag){try{this.wait();}catch(Exception e){}}System.out.println(Thread.currentThread().getName()+"........消费........"+this.name);flag=false;this.notifyAll();}}class Producer implements Runnable{private Resource res;Producer(Resource res){this.res=res;}public void run(){while(true){res.set("+商品+");}}}class Consumer implements Runnable{private Resource res;Consumer(Resource res){this.res=res;}public void run(){while(true){res.out();}}}class  ThreadProCus1{public static void main(String[] args) {Resource res=new Resource();Producer pro=new Producer(res);Consumer con=new Consumer(res);Thread t1=new Thread(pro);Thread t2=new Thread(pro);Thread t3=new Thread(con);Thread t4=new Thread(con);t1.start();t2.start();t3.start();t4.start();}}
总结:
1,对于多个生产和消费:
为什么要定义while判断标记。
原因:让被唤醒的线程再一次判断标记。
2,为什么定义notifyAll().
因为需要将对方的线程唤醒。
因为只用notify(),容易出现只唤醒本方的线程的情况,导致程序中的所有的线程都在等待。 
2.7.4 多线程——生产者和消费者的改进-Lock接口
在上一个代码中,存在一个bug, 就是notifyAll(), 可能会将本方的线程也给唤醒, 如何才能让他不换醒呢?
接口中Lock,特点是替代了synchronized,比使用synchronized方法和语句可获得更广泛的锁定操作。
首先:synchronized,开锁解锁都是隐式的, 而lock就可以一目了然的看到,他是显示的, 
并且可以支持多个相关的Condition对象。
Condition把wait,notify,notifyAll,替代,因此可以将代码改进:

/*用synchronized,开锁解锁是隐式的, 但是用lock , 就可以一目了然了, 显式的,*/import java.util.concurrent.locks.*;class Resource{private String name;private int count=1;private boolean flag=false;private Lock lock=new ReentrantLock();//多态, 创建一个锁对象,private Condition condition_pro=lock.newCondition();//创建condition的对象private Condition condition_con=lock.newCondition();public void set(String name)throws InterruptedException{//将同步的语句变成了两个方法lock(),unlock().lock.lock();//调用lock()方法,获取了一个锁//用if判断, 数据错误, while循环,全部等待, 不是活着的, 冻结了try{while(flag){condition_pro.await();}this.name=name+"....."+count++;System.out.println(Thread.currentThread().getName()+"....生产...."+this.name);flag=true;condition_con.signal();}finally{lock.unlock();//解锁}}public void out()throws InterruptedException{lock.lock();try{while(!flag){condition_con.await();}System.out.println(Thread.currentThread().getName()+"........消费........"+this.name);flag=false;condition_pro.signal();}finally{lock.unlock();}}}class Producer implements Runnable{private Resource res;Producer(Resource res){this.res=res;}public void run(){while(true){try{res.set("+商品+");}catch (InterruptedException e){}}}}class Consumer implements Runnable{private Resource res;Consumer(Resource res){this.res=res;}public void run(){while(true){try{res.out();}catch (InterruptedException e){}}}}class  ThreadProCus2{public static void main(String[] args) {Resource res=new Resource();Producer pro=new Producer(res);Consumer con=new Consumer(res);Thread t1=new Thread(pro);Thread t2=new Thread(pro);Thread t3=new Thread(con);Thread t4=new Thread(con);t1.start();t2.start();t3.start();t4.start();}}
2.8 多线程——停止线程
首先开启多个线程运行, 一般运行的代码通常是循环结构。如果需要让线程停止, 
只要控制住循环, 就可以让run方法结束了。
让线程停止,这里用到的是intterupt(),以前用的是stop(),但是目前已经停用了。
intterupt()表示强制清除冻结状态,让线程回复到运行状态中来,不是结束线程。
相当于:这时把他给催眠(wait())了, 但是这时催眠的人出国了, 我来了, 一砖头(intterupt())下去,
受伤了, 发生异常了,叫做人家不该醒, 你让他醒,就异常了。
但是这样还是不能让程序结束, 但是能让他执行, 就离程序结束不远了,
在异常的处理语句中, 让flag变成false, 这样while循环就结束了, 那么这个线程就结束了,
代码如下:
class StopThread implements Runnable{private boolean flag=true;public synchronized void run(){while(flag){try{wait();//这样线程处于冻结状态,主函数结束,但是程序没有结束//本来冻结了,但是一砖头(interrupt()),打醒了,就抛出异常}catch (InterruptedException e){System.out.println(Thread.currentThread().getName()+".....Exception");flag=false;//一砖头打醒后,会执行,那么while就不会执行了,}System.out.println(Thread.currentThread().getName()+".....run");}}public void changeFlag(){flag=false;}}class Threadstop{public static void main(String[] args) {StopThread st=new StopThread();Thread t1=new Thread(st);Thread t2=new Thread(st);t1.start();t2.start();int num=0;while(true){if(num==60){t1.interrupt();t2.interrupt();//st.changeFlag();break;}System.out.println(Thread.currentThread().getName()+"...."+num++);}System.out.println("over");}}
总结:
当没有指定的方式让冻结的线程恢复到运行的状态时, 这时, 需要对冻结进行清除,强制让线程恢复到运行状态中来, 这样就可以让操作标记让线程结束,
Thread类中提供了该方法,Intertupt。
2.9 多线程——守护线程,join方法
守护线程:也叫后台线程,他需要在线程创建后, 就需要设置。
前台线程:所看到的线程都是前台的线程。
然后在开始执行线程。
在运行的时候, 会共同抢夺CPU的执行权运行, 开启运行时没有区别的;
但是在结束程序上有区别:
当前台的线程都结束后, 后台的线程就会自动结束。
join方法:等待该线程终止。

代码如下:
//线程0, 要申请加入进来, 线程0要CPU的执行权,join的意思就是, 抢夺CPU的执行权。//这时主线程就把CPU的执行权给了线程0,主线程处于冻结状态//数据全部打印完, 结束后,主线程才恢复到运行状态,这个就是join 的用法,//主线程让出了执行权,这时主线程会只等到t1线程执行完,才活过来, 和t2结不结束, 没有关系,//join可以临时加入线程执行class Demo implements Runnable{public void run(){for(int x=0;x<70;x++){System.out.println(Thread.currentThread().toString()+"...."+x);Thread.yield();//暂停当前执行的线程程序}}}class ThreadJoin {public static void main(String[] args)throws InterruptedException{Demo d=new Demo();Thread t1=new Thread(d);Thread t2=new Thread(d);t1.start();//t1.setPriority(Thread.MAX_PRIORITY);//设置优先级t2.start();//t1.join();/*for(int x=0;x<80;x++){System.out.println(Thread.currentThread().getName()+"...."+x);}*/}}
2.10 多线程——优先级,yield
多线程的优先级:设置setPriority();
把优先级设高点, 但是对于CPU的抢夺, 抢夺的概率会大一点。
yield()方法:
表示暂停当前正在执行的线程对象,并执行其他的线程。
线程释放了CPU的执行权, 就停下了, 另一个线程进来,又停下,这样交替, 
稍微减缓线程的频率, 交替进行,达到线程都有机会运行。如果不这样, 一个线程会输出好几次;














 

3.png (14.26 KB, 下载次数: 0)

下载附件  保存到相册

昨天 08:34 上传




























 -------------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

0 0
原创粉丝点击