黑马程序员——自学总结(一):多线程

<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流!

 

一、线程的概念

        进程是一个正在执行中的程序,每一个进程执行都有一个执行顺序,或者叫一个控制单元,线程就是进程中的一个独立的控制单元,线程在控制着进程的执行,一个进程中可能同时有多个线程在运行,比如迅雷下载。一个进程中至少有一个线程,JavaVM启动时会有一个进程java.exe,该进程至少有一个线程, 负责java程序的执行,而且这个线程运行的代码存在于main方法中,该线程称之为主线程 。(其实更细节说明JVM,JVM启动不至一个线程,还有负责垃圾回收机制的线程。)CPU某一时刻处理一个线程,并且在线程间做着高速的切换,让我们感觉各线程是在同时运行。多核可以实现真正的同时处理,其瓶颈就是内存大小。

二、线程的创建

       1、第一种方式:继承Thread类

             通过对API的查找,Java已经提供了对线程这类事物的描述,就是java.lang包中的Thread类,可以通过继承Thread类来创建线程。

            步骤:

            (1)定义类继承Thread

            (2)复写Thread类中的run方法

            (3)调用线程的start方法,该方法有两个作用:启动线程,调用run方法

            之所以要在start方法中调用run来运行线程,我想应该是start方法在调用run方法之前,通过调用操作系统函数做了诸如分配线程资源、进入线程队列之类的操作,那么为什么JVM不直接在编译时直接在run方法代码中加上这些过程呢?我想可能是Java设计者们故意留下了一种可以直接调用run方法而不启动线程的方式。 

               

class ThreadDemo extends Thread{
	public void run(){
		for(int i=0;i<100;i++)
			System.out.println(getName()+"-----HelloWorld!-----"+i);
	}
	public static void main(String[] args){
		Thread t1=new ThreadDemo();
		Thread t2=new ThreadDemo();
 		t1.start();
		t2.start();	
	}
}


  程序运行结果:

          

多次运行后,发现运行结果每一次都不同,因为多个线程都可以获取CPU执行权,CPU执行到谁,谁就执行在某一时刻,只能有一个线程在运行,CPU在做着快速的切换,以达到看上去是同时运行的效果,我们可以形象地把多线程的运行行为看成是在互相抢夺CPU的执行权。这就是多线程的一个特性:随机性,谁抢到谁执行,至于执行多长,CPU说了算。

start方法与run方法的区别:start开启线程并执行该线程的run方法,run仅仅是对象调用方法,而线程创建了,并没有运行。

上个程序中,如果改成t1.run();t2.run(),程序结果变为t1先执行完输出100个“HelloWorld”,t2再输出100个“HelloWorld”,没有达到同步效果。

线程有如下四种状态:

   new()                       start()            sleep()或wait()

————>被创建————>运行————————>冻结

                                                          <———————

                                                         sleep时间到或notify()             

                                stop()或run方法结束                                                                                        

                                ——————————> 消亡

 

 

此外还可以有临时状态或叫做阻塞状态:具备运行资格,但没有执行权,线程冻结意味着线程放弃了执行资格,同时也就放弃了执行权,当sleep时间到或线程被notify后,线程转为临时状态,获得执行资格,等待执行权进入运行状态。

getName():获得线程名

线程有自己的默认名:Thread+编号,可以在线程初始化传递

Thread.currentThread();静态方法,获取当前线程对象

setName(String name)或构造函数设置线程名称

       2、第二种方式:实现Runnable接口

例子:多个窗口同时卖票

class Ticket extends Thread{
	private int n=100;
	public void run(){
		while(n>0)
			System.out.println(getName()+"-----sales-----"+n--);
	}
	public static void main(String[] args){
		Ticket t1=new Ticket(),
                       t2=new Ticket(),
		       t3=new Ticket(),
		       t4=new Ticket();
		t1.start();t2.start();t3.start();t4.start();
	}
}

程序运行结果:

结果发现0号窗口和2号窗口同时售出了61号票

因为主函数建立4个Ticket对象,结果各卖各的,重复卖票。如果建立1个Ticket对象,分四次启动start(),则线程状态异常,无法重复启动线程,从而引出创建线程的另一种方式:实现Runnable接口。

步骤:

(1)定义类实现Runnable接口

(2)覆盖Runnable接口中的run方法

(2) 继承Thread类建立线程对象

(4)将Runnable接口的子类对象作为实际参数传递给Thread子类的构造函数

(5)调用Thread子类的start方法开启线程并调用Runnable接口子类的run方法

实现方式和继承方式有什么区别呢?继承Thread线程代码存放在Thread子类run方法中,而实现Runnable线程代码存放在接口的子类的run方法,实现方式避免了单继承的局限性,在定义线程时,建议使用实现方式。

 三、多线程的安全问题

修改上面卖票例子的代码:

class Ticket implements  Runnable{
	private int n=1000;
	public void run(){
		while(n>0)
			System.out.println(Thread.currentThread().getName()+"-----sales----
			"+n--);
	}
	public static void main(String[] args){
		Ticket t=new Ticket();
		Thread t1=new Thread(t),
                       t2=new Thread(t),
		       t3=new Thread(t),
		       t4=new Thread(t);
		t1.start();t2.start();t3.start();t4.start();
	}
}


运行后,可能会出现两种异常情况:

(1)0号窗口卖出了49号票,接着1号窗口卖出50号票(应该先卖大票,再卖小票)

(2)某个窗口卖出了0号票,甚至卖出了-1号票

(本人试了好多次,都没有出现上述情况,估计是CPU太强大笑,或者是人品太差尴尬

 通过分析,发现,System.out.println()语句其实要执行两个步骤,执行n--和调用DOS命令窗口输出。那么问题来了,假设t1某一时刻判断n=50进入循环体,n-1变成49,即将打印50,突然被冻结,t2获得CPU,判断n=49,n-1变成48,打印出了49,之后t1获得了CPU,将刚才没打印的50打印出来,这样就出现了情况(1)。假设某一时刻n=1,t1判断完n>0突然被冻结,t2获得CPU,判断n=1,进入循环体,n-1变成0,输出1号票,然后t1继续执行,n-1=-1,输出0号票,出现情况(2)。总结就是:当多条语句在操作同一个线程共享数据时,一个线程被多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据错误。

解决办法:对多条操作共享数据的语句只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。JAVA对于多线程的安全问题提供了专业的解决方式,这就是同步代码块

synchronized(对象){

需要被同步的代码

}

修改上面卖票的例子:

class Ticket implements  Runnable{
	private int n=1000;
        private Object obj=new Object();
	private boolean flag=true;
	public void run(){
		    while(flag){
		    	synchronized(obj){
			   if(n>0){
				System.out.println(Thread.currentThread().getName()+"-----   

                              sales-----"+n--);
			   }else{
			        flag=false;	
                           }
		     	}
		    }
	}
	public static void main(String[] args){
		Ticket t=new Ticket();
		Thread t1=new Thread(t),
                       t2=new Thread(t),
		       t3=new Thread(t),
		       t4=new Thread(t);
		t1.start();t2.start();t3.start();t4.start();
	}
} 
 

同步代码块中锁可以是任意对象,但必须是同一个对象。

如何找出程序中有可能因同步出现的安全问题:

(1)明确哪些代码是多线程运行代码

(2)明确共享数据

(3)明确多线程运行代码中哪些语句是操作共享数据的

同步代码块封装了运行代码,而函数也具有封存代码的功能,于是可以用同步函数来代替同步代码块,同步函数使用的锁是this,即对象本身。

验证:使用两个线程来卖票,一个同步代码块,一个同步函数,都在执行卖票工作,用flag确定线程执行路径

class Ticket implements  Runnable{
	private int n=1000;
	public boolean flag=true;
	private Object obj=new Object();
	private boolean over=false;
	public void run(){
		 if(flag){
		    synchronized(this){
			while(!over)
			 if(n>0){
			    System.out.println(Thread.currentThread().getName()+"-----sales-----"+n--);
			    flag=!flag;
			 }else{
			    over=!over;
			 }
		     }
		} else{
			show();
			flag=!flag;
		 }
	}
	private synchronized void show(){
	   while(!over)
	    if(n>0)
	        System.out.println(Thread.currentThread().getName()+"-----sales-----"+n--);
	    else
                over=!over;
	}
	public static void main(String[] args){
		Ticket t=new Ticket();
		Thread t1=new Thread(t),
                       t2=new Thread(t);
		t1.start();
		try{
			Thread.currentThread().sleep(100);
		}catch(Exception e){}
		t.flag=false;
		t2.start();
	}
}

运行正常,如果将同步代码块中的this改成obj,就会出现问题,说明同步函数使用的锁是this,即对象本身。还可以验证静态同步函数的锁是Class对象(静态方法中也没有this),静态方法进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象

类名.class,该对象的类型是Class,静态同步方法使用的锁就是该方法所在类的字节码文件对象。

在单例设计模式中,如果涉及到多线程,也可能会出现安全问题。单例设计模式有懒汉式和饿汉式两种,先看懒汉式:

class Single{
	private static s=null;
	private Single(){
		System.out.println("new Single()");
	}
	public static Single getInstance(){
		if(s==null)
			s=new Single();
		return s;
	}
}
    class SingleDemo implements Runnable{
	public void run(){
		Single.getInstance();	
	}
	public static void main(String[] args){
		SingleDemo s=new SingleDemo();
         	Thread t1=new Thread(t),t2=new Thread(t), 
                       t3=new Thread(t),t4=new Thread(t); 
		t1.start();t2.start();t3.start();t4.start();
       }
}

如果多个线程创建Single对象,就会出现问题。假设t1某时刻进入getInstance(),判断s=null,突然被冻结,t2获得CPU执行权,进入getInstance(),判断s=null,new Single();打印信息,之后t1获得CPU执行权,继续执行new  Single();打印信息,Single类就被new了两个对象出来。 解决办法就是加同步

class Single{
	private static s=null;
	private Single(){
		System.out.println("new Single()&quot);
        }
	public static Single getInstance(){
                synchronized(Single.class){
		  if(s==null)
			s=new Single();
		}
                return s;
       }		
}

如果t1先getInstance(),获得锁,t2只能等待,t1创建对象完毕,交出锁,t2获得锁,判断s!=null,返回,t3又重复获得锁,浪费资源,可在获得锁之前先判断是否已创建,避免反复判断锁

class Single{
	private static s=null;
	private Single(){
		System.out.println("new Single()");
	}
	public static Single getInstance(){
                if(s==null){
		  synchronized(Single.class){
	              if(s==null)
			s=new Single();
		}
                return s;		
}

饿汉式代码

class Single{
	private static Single s=new Single();
	private Single(){
		System.out.println("new Single()");
	}
	public static Single getInstance(){
		return s;
	}
}
多线程中还可能会出现另一个安全问题,就是死锁,出现死锁的原因是同步代码中嵌套同步代码.

class DeadLock implements Runnable{
	public boolean flag=true;
	private Object locka=new Object(),lockb=new Object();
	public void run(){
           while(true){
		if(flag){
		     synchronized(locka){
		   	show1();
		     }
		     flag=!flag;
		}else{
		    synchronized(lockb){
		   	show2();
	            }
		    flag=!flag;
		}
	   }
	}	
        private void show1(){
		synchronized(lockb){		
		}
	}
	private void show2(){
		synchronized(locka){			
		}
	}
	public static void main(String[] args){
		DeadLock d=new DeadLock();
		Thread t1=new Thread(d),t2=new Thread(d);
		t1.start();
		try{
		   Thread.currentThread().sleep(100);
		}catch(Exception e){}
		d.flag=false;
		t2.start();	
	}	
}


上述代码中,t1获得locka,t2获得lockb,之后t1请求lockb,但同时t2请求locka,发生死锁,编程应避免出现这种情况。

 四、多线程间的通信

      以上例程中线程运行代码都一致,现在看代码不一致的。有两个线程Input和Output,有一个资源类Res,包括成员变量String name和String  gender,Input负责往Res中存放name和gender,Output负责输出。

Input线程:

class Input implements Runnable{
	private Res res;
	private boolean flag=false;
	public Input(Res res){
		this.res=res;	
	}
	public void run(){
		while(true){
			if(flag){
				res.set("刘德华","男");
			}else{
				res.set("刘亦菲","女");
			}
			flag=!flag;
		}
	}
}


Output线程

class Output implements Runnable{
	private Res res;
	public Output(Res res){
		this.res=res;	
	}
	public void run(){
		while(true)
			res.get();
	}
}


Res类

class Res{
	private String name;
	private String gender;
	public String getName()
	{
		return name;	
	}
	public String getGender()
	{
		return gender;	
	}
	public void set(String name,String gender){
		this.name=name;
		this.gender=gender;
	
	}
	public void get(){
		System.out.println("name:"+getName()+"-----gender:"+getGender());
	}  
	public static void main(String[] args){
		Res res=new Res();
		Input in=new Input(res);
		Output out=new Output(res);
		Thread input=new Thread(in),output=new Thread(out);
		input.start();output.start();
	}
}


运行结果:

结果出现人妖刘德华,女和刘亦菲,男。出现这种错误的原因是线程间没有同步,当input线程存数据时,output不应输出,应等到input存完后才能输出

修改代码如下:

input线程

class Input implements Runnable{
	private Res res;
	private boolean flag=false;
	public Input(Res res){
		this.res=res;	
	}
	public void run(){
		while(true){
			synchronized(res){
			  if(flag){
				  res.set("刘德华","男");
			  }else{
				  res.set("刘亦菲","女");
			  }
			  flag=!flag;
			}
		}
	}
}


output线程

class Output implements Runnable{
	private Res res;
	public Output(Res res){
		this.res=res;	
	}
	public void run(){
		while(true){
		   synchronized(res){
			res.get();
		   }
		}
	}
}


运行结果如下:

程序流程应该是inout和output交替输入和输出,但运行结果不是这样,分析原因,是因为当input输入完释放锁后,可能下一个获得锁的依然是input,必须在输入数据前,判断res是否已被取走,引入等待唤醒机制,当数据没被取走,input就wait,直到某时刻被output用notify唤醒

修改代码:

input线程

class Input implements Runnable{
	private Res res;
	private boolean flag=false;
	public Input(Res res){
		this.res=res;	
	}
	public void run(){
		while(true){
			synchronized(res){
                          if(!res.empty)
			     try{
				res.wait();
			   }catch(Exception e){}
			  if(flag){
				  res.set("刘德华","男");
			  }else{
				  res.set("刘亦菲","女");
			  }
			  flag=!flag;
			  res.empty=!res.empty;
			  res.notify();
			}
		}
	}
}


output线程

class Output implements Runnable{
	private Res res;
	public Output(Res res){
		this.res=res;	
	}
	public void run(){
		while(true){
		   synchronized(res){
			if(res.empty)
			   try{
				res.wait();
			   }catch(Exception e){}
			res.get();
			res.empty=!res.empty;
			res.notify();
		   }
		}
	}
}


Res类

class Res{
	private String name;
	private String gender;
	public boolean empty=false;
	public String getName()
	{
		return name;	
	}
	public String getGender()
	{
		return gender;	
	}
	public void set(String name,String gender){
		this.name=name;
		this.gender=gender;
	
	}
	public void get(){
		System.out.println("name:"+getName()+"-----gender:"+getGender());
	}  
	public static void main(String[] args){
		Res res=new Res();
		Input in=new Input(res);
		Output out=new Output(res);
		Thread input=new Thread(in),output=new Thread(out);
		input.start();
		output.start();
	}
}


运行结果如下:

运行成功

以上的例程说明了同步线程间通讯的解决方案,线程间通讯其实就是多个线程在操作同一个资源,但是操作的动作不同。上面的例子其实是“生产者——消费者”同步通讯问题的一个特例,即生产者和消费者都只是一个人,下面分析多个生产者和多个消费者的情况。

假设有2个生产者0号线程,1号线程和2个消费者2号线程,3号线程,生产者即input,消费者即output,修改程序:

为了区分生产者和消费者,每次输入或输出时,同时输出当前线程的名称

input线程

class Input implements Runnable{
	private Res res;
	private boolean flag=false;
	public Input(Res res){
		this.res=res;	
	}
	public void run(){
		while(true){
			synchronized(res){
                          if(!res.empty)
			     try{
				res.wait();
			   }catch(Exception e){}
			  if(flag){
				  System.out.println(Thread.currentThread().getName()+"生产-----刘德华-----男");
				  res.set("刘德华","男");
			  }else{  
				  System.out.println(Thread.currentThread().getName()+"生产-----刘亦菲-----女");
				  res.set("刘亦菲","女");
			  }
			  flag=!flag;
			  res.empty=!res.empty;
			  res.notify();
			}
		}
	}
}


output线程

class Output implements Runnable{
	private Res res;
	public Output(Res res){
		this.res=res;	
	}
	public void run(){
		while(true){
		   synchronized(res){
			if(res.empty)
			   try{
				res.wait();
			   }catch(Exception e){}
			System.out.print(Thread.currentThread().getName()+"消费-----");
			res.get();
			res.empty=!res.empty;
			res.notify();
		   }
		}
	}
}

Res类

class Res{
	private String name;
	private String gender;
	public boolean empty=false;
	public String getName()
	{
		return name;	
	}
	public String getGender()
	{
		return gender;	
	}
	public void set(String name,String gender){
		this.name=name;
		this.gender=gender;
	
	}
	public void get(){
		System.out.println("name:"+getName()+"-----gender:"+getGender());
	}  
	public static void main(String[] args){
		Res res=new Res();
		Input in0=new Input(res);
		Thread input0=new Thread(in0);
		Input in1=new Input(res);
		Thread input1=new Thread(in1);

		Output out2=new Output(res);
		Thread output2=new Thread(out2);
		Output out3=new Output(res);
		Thread output3=new Thread(out3);
	
		input0.start();input1.start();
		output2.start();output3.start();
	}
}

程序运行结果如下:

分析下这段代码,当某一时刻res没有新数据,此时output2执行,获得锁,判断没有新数据,等待,output3执行,获得锁,判断没有新数据,等待,input0执行,获得锁,输入新数据,唤醒等待队列中第一个线程output2,output2取走新数据,接着唤醒等待队列上第二个线程output3,output3继续取数据,这样output2之前已取走的数据被重复取走了。原因在于当output3被唤醒时,没有重新判断是否有新数据,修改代码仅需把判断语句if改成while,保证每次被唤醒时重新判断一次,修改后运行,结果程序进入无限等待状态。原因如下:当某一时刻res数据未被取走,此时input0执行,获得锁,判断数据没取走,等待,input1执行,同样等待,output2执行,取走数据,唤醒input0,等待,input0输入新数据,唤醒input1,input1发现新数据,等待,接下来没有任何线程可以获得执行资格,程序进入无限等待状态。解决办法是当input0输入新数据后,唤醒所有线程,让output2获得执行资格,将数据取走。

最终程序代码如下:

input线程

class Input implements Runnable{
	private Res res;
	private static boolean flag=false;
	public Input(Res res){
		this.res=res;	
	}
	public void run(){
		while(true){
			synchronized(res){
                          while(!res.empty)
			     try{
				res.wait();
			   }catch(Exception e){}
			  if(flag){
				  System.out.println(Thread.currentThread().getName()+"生产

-----刘德华-----男");
				  res.set("刘德华","男");
			  }else{  
				  System.out.println(Thread.currentThread().getName()+"生产

-----刘亦菲-----女");
				  res.set("刘亦菲","女");
			  }
			  flag=!flag;
			  res.empty=!res.empty;
			  res.notifyAll();
			}
		}
	}
}


oputput线程

class Output implements Runnable{
	private Res res;
	public Output(Res res){
		this.res=res;	
	}
	public void run(){
		while(true){
		   synchronized(res){
			while(res.empty)
			   try{
				res.wait();
			   }catch(Exception e){}
			System.out.print(Thread.currentThread().getName()+"消费-----");
			res.get();
			res.empty=!res.empty;
			res.notifyAll();
		   }
		}
	}
}


Res类

class Res{
	private String name;
	private String gender;
	public boolean empty=false;
	public String getName()
	{
		return name;	
	}
	public String getGender()
	{
		return gender;	
	}
	public void set(String name,String gender){
		this.name=name;
		this.gender=gender;
	
	}
	public void get(){
		System.out.println("name:"+getName()+"-----gender:"+getGender());
	}  
	public static void main(String[] args){
		Res res=new Res();
		Input in0=new Input(res);
		Thread input0=new Thread(in0);
		Input in1=new Input(res);
		Thread input1=new Thread(in1);

		Output out2=new Output(res);
		Thread output2=new Thread(out2);
		Output out3=new Output(res);
		Thread output3=new Thread(out3);
	
		input0.start();input1.start();
		output2.start();output3.start();
	}
}

JDK5.0版本中对上述多生产者—多消费者问题给出了更优化的解决方案,在同步代码块中,可以利用condition选择性的唤醒线程。
代码如下:

input线程

import java.util.concurrent.locks.*;
class Input implements Runnable{
	private Res res;
	private static boolean flag=true;
	public Input(Res res){
		this.res=res;	
	}
	public void run(){
		while(true){
			  res.lock.lock();
			  try{
                            while(!res.empty)
				  res.con_in.await();
			    if(flag){
				    System.out.println(Thread.currentThread().getName()+"生

产-----刘德华-----男");
				    res.set("刘德华","男");
			    }else{  
				    System.out.println(Thread.currentThread().getName()+"生

产-----刘亦菲-----女");
				    res.set("刘亦菲","女");
			    }
			    flag=!flag;
			    res.empty=!res.empty;
			    res.con_out.signal();
			  }catch(InterruptedException e){
			  }finally{
			    res.lock.unlock();
                          }
			
		}
	}
}


output线程

import java.util.concurrent.locks.*;
class Output implements Runnable{
	private Res res;
	public Output(Res res){
		this.res=res;	
	}
	public void run()  {
		while(true){
		   res.lock.lock();
		   try{
			while(res.empty)
			    res.con_out.await();
			System.out.print(Thread.currentThread().getName()+"消费-----");
			res.get();
			res.empty=!res.empty;
			res.con_in.signalAll();
		   }catch(InterruptedException e){
		   }finally{
		   	res.lock.unlock();
                   }
		}
	}
}


Res类

import java.util.concurrent.locks.*;
class Res{
	public boolean empty=true;
	private String name,gender;
	public Lock lock=new ReentrantLock();
	public Condition  con_in=lock.newCondition(),con_out=lock.newCondition();
	public void set(String name,String gender){
		this.name=name;
		this.gender=gender;
	}
	public void get(){
		System.out.println("name:-----"+name+"gender:-----"+gender);
	}
  
	public static void main(String[] args){
		Res res=new Res();
		Input in0=new Input(res);
		Thread input0=new Thread(in0);
		Input in1=new Input(res);
		Thread input1=new Thread(in1);

		Output out2=new Output(res);
		Thread output2=new Thread(out2);
		Output out3=new Output(res);
		Thread output3=new Thread(out3);
	
		input0.start();input1.start();
		output2.start();output3.start();
	}
}


五、守护线程和几个常见的线程方法

        1、线程停止

              开启多线程运行,运行代码通常是循环结构,只要控制住循环,就可以让run方法结束,也就是让线程结束。我们可以在线程中设置结束标志位,循环判断,在主线程中确定标志位什么时候为假,从而结束线程。

class StopThread implements Runnable{
	public boolean go=true;
	private int x=0;
	public void run(){
		while(go){
			System.out.println("go..."+x++);
		}
		System.out.println("stop!");
	}
	public static void main(String[] args){
		StopThread stop=new StopThread();
		Thread t=new Thread(stop);
		t.start();
		int i=0;
		while(true)
		   if(i++==100)
			stop.go=false;
	}
}

但是如果线程run方法中执行了wait(),线程将处于冻结状态,不会读取到go标记,也就无法停止,可以通过调用线程的interrupt方法将已冻结的线程重新进入运行状态。调用interrupt方法将会使程序捕获到InterruptedException异常,通过在异常处理代码中设置标志位,可以让线程重新回到运行状态,线程继续判断标志位从而结束线程。

        2、守护线程

             守护线程也就是后台线程,开启后和前台线程共同争夺CPU执行权,当所有的前台线程都结束后,后台线程会自动结束,setDaemon()方法设置线程为守护线程,该方法必须在线程启动前即start前使用。

      3、join方法

           线程调用join方法后,当前线程放弃执行权,该线程获得CPU执行权,直到该线程运行结束,之前放弃执行权的线程才重新抢夺执行权,其他线程不受影响。

      4、线程优先级和yield方法

          可以通过setPriority(int)方法改变线程的优先级,优先级是建议CPU分配执行权的数值,范围为1—10,数值越大,优先级越高,默认值是Thread.NORM_PRIORITY=5,最大值是Thread.MAX_PRIORITY=10,最小值是Thread.MIN_PRIORITY=0。

       yield()方法让线程暂时放弃执行权,让其他线程有机会执行,可以减缓线程的执行速度,让所有线程和谐运行。微笑



 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值