多线程


一、理解多线程相关的概念
    进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。
    线程:就是进程中的一个独立的控制单元。线程在控制着进程的执行。
    二者关系:一个进程中至少有一个线程。线程是进程中的内容。
    Java VM 启动的时候会有一个进程java.exe.该进程中至少一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。
    扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程。
二、线程的状态


三、自定线程
1、第一种方式,继承Thread类。
   通过对api的查找,java已经提供了对线程这类事物的描述。就是Thread类。创建线程的第一种方式:继承Thread类。


(1)创建步骤:
1,定义类继承Thread。
2,复写Thread类中的run方法。
  目的:将自定义代码存储在run方法。让线程运行。
3,调用线程的start方法,
           该方法两个作用:启动线程,调用run方法。

//自定义线程
class Trd extends Thread{//创建一个线程
	public void run(){//覆盖run方法
		for(int i=0;i<50;i++)
			System.out.println("---------------run"+i);
	}
}
class T1 {
	public static void main(String[] args) {
		Trd t=new Trd();
		Trd t1=new Trd();
		Trd t2=new Trd();
		t.start();//开启线程,此时它与主线程并存
		t1.start();
		for(int i=0;i<50;i++)
			System.out.println("-------main------"+i);
		t2.run();//只是调用函数方法,虽然建立了线程,但是没有开启
		}}
    上述代码发现运行结果每一次都不同。因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)cpu在做着快速的切换,以达到看上去是同时运行的效果。我们可以形象把多线程的运行行为在互相抢夺cpu的执行权。这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长,cpu说的算。
为什么重写run方法:
Thread类用于描述线程。该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。也就是说Thread类中的run方法,用于存储线程要运行的代码。
Thread t=new Thread();t就是一个线程,但是没有意义,因为它的run方法是Thread类的方法。

(2)Thread类中的几个方法

   线程都有自己默认的名字,Thread-编号(编号从0开始)
   Static Thread currentThread():获取当前线程对象
   getName():获取线程名称
   setName() 或者 构造函数来设置线程名称

class Trd1 extends Thread{
	Trd1(String d){
		super(d);//调用父类构造函数,给线程起名字
	}
	Trd1(){
		}
	public void run(){
		for(int i=0;i<30;i++){
System.out.println(Thread.currentThread()+"+++++++++"+this.getName()+"----"+i);
		}
	}}
public class T2 {
	public static void main(String[] args) {
		Trd1 t0=new Trd1("哈哈哈哈哈");
		Trd1 t1=new Trd1("哈哈哈哈哈");
		Trd1 t2=new Trd1();
		Trd1 t3=new Trd1();
		t1.setName("hehehehhehehehehehhehehe");//t1重命名
		//开启线程
		t0.start();
		t1.start();
		t2.start();
		t3.start();
		for(int i=0;i<30;i++){
		System.out.println("main"+i+Thread.currentThread());
		}
		t2.setName("222222222");
	}

}
//简单的卖票

class Trd3 extends Thread{
	private static int num=100;//为了让多个线程共用num,所以用静态
	public void run(){
		/*for(;num>=1;num--)
		{
			System.out.println(this.getName()+"剩余---"+(num-1));
		}
		这种方式会出现俩99或者三个99的原因就是1个线程执行到打印语句时,2线程在
		打印语句之前等待,1线程打印完还没执行num--,2线程就抢到CPU执行打印语句
		所以会出现重复
		*/
		while(num>0){
			System.out.println(this.getName()+"剩余---"+(--num));
		}//这种方式不会重复,但可能会出现负数
	}
}
public class T3 {
public static void main(String[] args) {
	Trd3 t1=new Trd3();
	Trd3 t2=new Trd3();
	Trd3 t3=new Trd3();
	
	t1.start();
	t2.start();
	t3.start();
	}
}
2、创建线程第二种方式,实现Runable接口
步骤:
1,定义类实现Runnable接口
2,覆盖Runnable接口中的run方法。
        将线程要运行的代码存放在该run方法中。
3,通过Thread类建立线程对象。
4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
  为什么要将Runnable接口的子类对象传递给Thread的构造函数。
  因为,自定义的run方法所属的对象是Runnable接口的子类对象。
  所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。
    5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。


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

class Trd4 implements Runnable{
	private  int num=100;
	public void run(){
		while(num>0){
			/*这种方式会出现负数,原因就是1个线程执行到打印语句时,2线程在
			打印语句之前等待,假设此时的num=0.1线程打印完,2线程就抢到CPU执行打印语句
			此时出现负数*/
			try{
			Thread.sleep(10);}//使当前线程冻结10ms,从而会有多于1个线程在此停留
			                   //以此来证明会出现负数
			catch(Exception e){}
			System.out.println(Thread.currentThread().getName()+"线程在执行,剩余@@@@"+(--num));
		}
	}
}
public class T4 {
public static void main(String[] args) {
	Trd4 t=new Trd4();
     //开启了六个线程
	
    new Thread(t).start();
	new Thread(t).start();
	new Thread(t).start();
	new Thread(t).start();
	new Thread(t).start();
	new Thread(t).start();

	}
}
上述代码可能会出现负数,多线程的运行出现了安全问题。


问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,
另一个线程参与进来执行。导致共享数据的错误。
 解决办法:

对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。


四、同步
   Java对于多线程的安全问题提供了专业的解决方式。就是同步代码块。
  synchronized(对象)(这个对象无要求)
{
  // 需要被同步的代码
}
  对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
火车上的卫生间---经典的同步的例子。或者把锁看成大门,每个线程都会随手关门,所以,进门就锁门,出去就开门。只要保证只有一个线程在门内。
 同步的前提:
  1,必须要有两个或者两个以上的线程。
  2,必须是多个线程使用同一个锁。
必须保证同步中只能有一个线程在运行。
    好处:解决了多线程的安全问题。
    弊端:多个线程需要判断锁,较为消耗资源,
 如何找问题:
1,明确哪些代码是多线程运行代码。
2,明确共享数据。
3,明确多线程运行代码中哪些语句是操作共享数据的。

(分开操作共享数据的语句之间,可以加sleep语句来进行测试。)

class Trd5 implements Runnable{
	private  int num=100;
	private boolean t=true;
	public void run(){
		while(t){
		synchronized("d"){//同步代码快,解决负数问题,明显感觉代码执行速度慢了许多
		    if(num>0){
			try{
			Thread.sleep(10);}
			catch(Exception e){}
			System.out.println(Thread.currentThread().getName()+"线程在执行,剩余@@@@"+(--num));
		    	}}
		    if(num==0)
		    	t=false;
		    }
		/*synchronized("d"){
		while(num>0){
				try{
				Thread.sleep(10);}
				catch(Exception e){}
				System.out.println(Thread.currentThread().getName()+"线程在执行,剩余@@@@"+(--num));
			    }}
			    这种方式显然不合适,因为只有一个线程执行while语句,就跟单线程一样*/
		
	}
}
public class T5 {
public static void main(String[] args) {
	Trd5 t=new Trd5();
    //开启了六个线程
	
    new Thread(t).start();
	new Thread(t).start();
	new Thread(t).start();
	new Thread(t).start();
	new Thread(t).start();
	new Thread(t).start();
}}
//金库
class Bank{
	private int sum;
	/*public void add(int i){
		synchronized("dd"){//同步代码快
		sum+=i;
		try{Thread.sleep(10);}
		catch(Exception s){}
		System.out.println(sum);}
	}*/
	public synchronized void add(int i){//同步函数
		sum+=i;
		try{Thread.sleep(10);}
		catch(Exception s){}
		System.out.println(sum);
	}
}
class P implements Runnable{
	private Bank k=new Bank();
	public void run(){
		for(int i=0;i<3;i++){
			k.add(100);
		}
	}
}
public class T6 {
	public static void main(String[] args) {
		P a=new P();
		new Thread(a).start();
		new Thread(a).start();
	}
}
将synchronized放在函数上,就是同步函数。所以同步分两种形式:同步代码快和同步函数
//卖票
class Trd7 implements Runnable{
	private  int num=10;
	private boolean t=true;
	public void run(){
		while(t){
		     show();
		    }
	}
		public synchronized void show(){//将同步代码快中的代码封装成函数,然后函数用synchronized修饰
			if(num>0){
				try{
				Thread.sleep(10);}
				catch(Exception e){}
				System.out.println(Thread.currentThread().getName()+"线程在执行,剩余@@@@"+(--num));
				 if(num==0)
				    	t=false;}}
	}
public class T7 {
	public static void main(String[] args) {
		Trd7 t=new Trd7();
	    //开启了六个线程
		
	    new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
	}}


同步函数用的是哪一个锁呢?
  函数需要被对象调用。那么函数都有一个所属对象引用。就是this。
  所以同步函数使用的锁是this。如果同步函数被静态修饰后,使用的锁是什么呢?通过验证,发现不在是this。因为静态方法中也不可以定义this。静态进内存是,内存中没有  本类对象,但是一定有该类对应的字节码文件对象 类名.class  该对象的类型是Class静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class

对上述进行验证:

//验证同步函数的锁
class Trd8 implements Runnable{
	private  static int num=200;
	private static boolean t=true;
	boolean b=true;
	public void run(){
		if(b){
			while(t){
				synchronized(this){//只有锁是this时,代码运行正常。当show方法为static时,只有锁为Trd8.class代码运行正常(T8.class也不正常)
				if(num>0){
					try{Thread.sleep(10);}catch(Exception e){}
					System.out.println(Thread.currentThread().getName()+"线程在执行,剩余@@@@"+(--num));
					 if(num==0)
					    	t=false;
					 }}
	    }}
		else
		while(t){
		     show();
		    }
	}
		public  synchronized void show(){
			if(num>0){
				try{
				Thread.sleep(10);}catch(Exception e){}
				System.out.println(Thread.currentThread().getName()+"-----show"+(--num));
				 if(num==0)
				    	t=false;}}
	}
 class T8 {
	public static void main(String[] args)throws Exception {
		System.gc();
		Trd8 t=new Trd8();
	    new Thread(t).start();
	    Thread.sleep(20);//让主线程睡眠10ms,目的就是让第一个线程运行,以防止b=false后两个线程都执行同步函数
	    t.b=false;
		new Thread(t).start();
	}}

死锁:

同步中嵌套同步。程序中应避免死锁出现。

//死锁
class Trd9 implements Runnable{
	private boolean b;
	Trd9(boolean b){
		this.b=b;
	}
	public void run(){
		if(b){
			synchronized(cl.o1){
				System.out.println("true---o1");
				synchronized(cl.o2){
					System.out.println("true---o2");
				}
			}
		}
		else{
			synchronized(cl.o2){
				System.out.println("false---o2");
				synchronized(cl.o1){
					System.out.println("false---o1");
				}
			}
		}
	}
}
class cl{
	static Object o1=new Object();
	static Object o2=new Object();
}
public class T9 {
	public static void main(String[] args) {
		new Thread(new Trd9(true)).start();
		new Thread(new Trd9(false)).start();
	}}

五、线程间通信

其实就是多个线程操作同一个资源,但是操作的动作不同

public class T10 
{
	public static void main(String[] args) 
	{
		Zi z=new Zi();
		new Thread(new In(z)).start();
		new Thread(new Out(z)).start();
	}
}

//描述被操作的资源
class Zi{
 String name;
 String sex;
}

class In implements Runnable
{
	private Zi z;
	In(Zi z)//这只是为了保证能够共用同一个资源
	{
		this.z=z;
	}
	public void run()
	{
		int x=0;
		
		while(true)
		{
			synchronized(z)
			{
				if(x==0)
				{
					z.name="lisi";
					z.sex="nan";
				}
				else
				{
					z.name="丽丽丽丽丽丽丽丽";
					z.sex="女女女女女";
				}
				x=(x+1)%2;
			}
		}
	}
}
class Out implements Runnable
{
	private Zi z;
	Out(Zi z)
	{
		this.z=z;
	}
	public void run()
	{
		while(true)
		{
			synchronized(z)
			{
				System.out.println(z.name+"-----"+z.sex);
			}
		}
	}
}
/*解析:首先同步前,创建俩线程发现打印结果有错误,那就考虑同步,当对In中的run方法内的操作
 * 资源的语句同步后,发现结果依然不对,这时就要紧扣同步的俩前提,发现Out中的打印语句也是
 * 操作资源,也需要被同步。为了保证锁是同一个锁,就用z对象。至此,打印结果解决了姓名性别不
 * 匹配的问题。但是,不能保证输入一个获取一个。因此要考虑使用等待唤醒机制。*/

等待唤醒机制:wait()、notify()、notifyAll();

都使用在同步中,因为要对持有监视器(锁)的线程操作。所以要使用在同步中,因为只有同步才具有锁.用法:锁.方法。 通常,等待线程都在线程池中,notify唤醒的是其中相同锁的第一个被等待的线程。Sleep方法不释放锁,wait方法释放锁。

为什么这些操作线程的方法要定义Object类中呢?

因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。

//使用等待唤醒机制,且对代码优化
public class T11 
{
	public static void main(String[] args) 
	{
		Z z=new Z();
		new Thread(new In1(z)).start();
		new Thread(new Out1(z)).start();
	}
}

//描述被操作的资源
class Z{
 private String name;
 private String sex;
 private boolean flag=false;
 public synchronized void set(String name,String sex)//同步函数,锁是this
 {
	 if(flag)
		 try{this.wait();}catch(Exception e){}//等待唤醒机制要在同步中使用
	 this.name=name;
	 this.sex=sex;
	 flag=true;
	 this.notify();
 }
 public synchronized void get()//同步函数,锁是this
 {
	 if(!flag)
		 try{this.wait();}catch(Exception e){}
	 System.out.println(this.name+"-----"+this.sex);
	 flag=false;
	 this.notify();
 }
}

class In1 implements Runnable
{
	private Z z;
	In1(Z z)//这只是为了保证能够共用同一个资源
	{
		this.z=z;
	}
	public void run()
	{
		int x=0;
		
		while(true)
		{
				if(x==0)
					z.set("zhangsan","nan");
				else
					z.set("丽丽丽丽丽丽丽丽","女女女女女");	
				x=(x+1)%2;
		}
	}
}
class Out1 implements Runnable
{
	private Z z;
	Out1(Z z)
	{
		this.z=z;
	}
	public void run()
	{
		while(true)
			z.get();	
	}
}
/*将资源中的属性全部私有,定义访问这些属性的方法。要保证输入一个输出一个的友好顺序,
 * 那就需要In执行一次,out执行一次,因此要定义一个判断标记flag,flag为真就输出,
 * 为假就输入。因此定义flag作为判断标记。因为为了保证俩线程用的同一个资源,所以
 * 只创建了一个资源对象,因此保证了set和get两个同步函数锁一致。又因为等待唤醒机
 * 制要在同步中使用,所以定义在这两个同步函数中,锁是this。
 * 注意:wait()会释放锁,sleep()不会*/

每个动作都开启多个线程:

//生产者和消费者
public class T12 {
	public static void main(String[] args) {
		Resource rec=new Resource();
	    new Thread(new Pro(rec)).start();
	    new Thread(new Pro(rec)).start();
	    new Thread(new Cons(rec)).start();
	    new Thread(new Cons(rec)).start();
	}	
}

//资源
class Resource
{
	private String name;
	private int count;
	private boolean flag=false;
	public synchronized void set(String name)
	{
		while(flag)//if(flag)
			try{this.wait();}catch(Exception e){}
		this.name=name+"---"+count++;
		System.out.println(Thread.currentThread().getName()+"---生产者---"+this.name);
	    flag=true;
	    this.notifyAll();//this.notify();
	}
	public synchronized void get()
	{
		while(!flag)//if(!flag)
			try{this.wait();}catch(Exception e){}
		System.out.println(Thread.currentThread().getName()+"-----xiaofeizhe-------"+this.name);
		flag=false;
		this.notifyAll();//this.notify();
	}
}

class Pro implements Runnable
{
	private Resource rec;
	Pro(Resource rec)
	{
		this.rec=rec;
	}
	public void run()
	{
		while(true)
			rec.set("商品");
	}
}
class Cons implements Runnable
{
	private Resource rec;
	Cons(Resource rec)
	{
		this.rec=rec;
	}
	public void run()
	{
		while(true)
			rec.get();
	}
}
/*解析一下:创建了多个线程来执行同一个动作。假如判断标记还是if语句。此时有0、1两个生产者线程,
 * 2、3两个消费者线程.结果会出现消费俩生产一个或者生产俩消费一个的情况
 * 第1、假如0有了执行权,flag=false,0先去执行name赋值语句和打印语句,flag=true,此时,
 *    0返回if语句,进入等待状态,释放执行权。
 * 第2、假如此时1线程得到执行权,进入if语句,也进入等待状态(0等待,1在if下等待)
 * 第3、此时2线程得到执行权,正常打印语句,之后flag=false,唤醒0线程,再次在if语句时,进入
 *      等待状态。(0被唤醒,1在if下等待,2等待)
 * 第4、假如0线程得到了执行权,0进行赋值和输出,此时1被唤醒,0线程继续执行if语句时进入等待状态,
 *     此时1线程将继续执行赋值语句和打印语句。问题就出现在这里,问题的根源就是1线程没有再次判断
 *     标记flag。
 * 所以,为了解决这个问题,将if换成while。
 * 但是,由于notify唤醒的是线程池中最先等待的线程,所以有时会唤醒本方线程,从而使所有线程都停在
 * 等待状态。所以,将notify换成notifyAll.唤醒所有线程,目的是唤醒对方线程。
 * 多个生产者和多个消费者要用while判断标记,用notifyall唤醒线程。*/

生产者和消费者:

对于多个生产者和消费者。

为什么要定义while判断标记。

原因:让被唤醒的线程再一次判断标记。

为什么定义notifyAll。

因为需要唤醒对方线程。因为只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。

 

                              

JDK1.5 中提供了多线程升级解决方案。将同步Synchronized替换成现实Lock操作。将Object中的wait,notify notifyAll,替换了Condition对象。该对象可以Lock锁 进行获取。该示例中,实现了本方只唤醒对方操作。

在java.util.concurrent.locks包中,有Lock接口,有了这个工具,从原来的一个锁对应一个wait、notify方法转变成一个锁可以对应多个await、signal方法。

Condition是一个接口,由Lock中的newCodition()方法获取对象。通常建立Lock子类对象的方法是 Lock lock=new ReentrantLock();

Lock:替代了Synchronized

       lock

       unlock  //该方法要在finally语句中

       newCondition()

Condition:替代了Object wait notify notifyAll

       await();

       signal();

       signalAll();

  注意,await方法会拋异常:InterruptedException

import java.util.concurrent.locks.*;
public class T13{
	public static void main(String[] args){
		Re re=new Re();
		//多个生产者和消费者
		new Thread(new Sc(re)).start();
		new Thread(new Sc(re)).start();
		new Thread(new Sc(re)).start();
		new Thread(new Sc(re)).start();
		new Thread(new Xf(re)).start();
		new Thread(new Xf(re)).start();
	}
}

class Re{
	private String name;
	private int count;
	private boolean flag;
	private Lock lock=new ReentrantLock();
	private Condition con1=lock.newCondition();
	private Condition con2=lock.newCondition();
	public void set(String name)throws InterruptedException 
	{
		lock.lock();//锁
		try{
			while(flag)//循环判断标记,以免出现不是一生产一消费的情况
			con1.await();
		this.name=name+"----"+count++;
		System.out.println(Thread.currentThread().getName()+"---生产者----"+this.name);
		flag=true;
		}
		finally{
		con2.signal();}//唤醒对方
	}
	public void get()throws InterruptedException 
	{
		lock.lock();
		try{
			while(!flag)
				con2.await();
			System.out.println(Thread.currentThread().getName()+"---xiaofei------------"+this.name);
			flag=false;
			}
		finally{
			con1.signal();
		}
	}
}
class Sc implements Runnable{
	private Re re;
	Sc(Re re){
		this.re=re;
	}
	public void run()
	{
		while(true)
			try{re.set("商品");}catch(InterruptedException i){}
	}
}
class Xf implements Runnable{
	private Re re;
	Xf(Re re){
		this.re=re;
	}
	public void run()
	{
		while(true)
			try{re.get();}catch(InterruptedException i){}
	}
}

六、停止线程、interrupt();方法

stop方法已经过时。那么如何停止线程?

只有一种,run方法结束。

开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。

例如:  
public class T14 {
	public static void main(String[] args) {
		Th t=new Th();
		Thread t1=new Thread(t);
        //t1.setDaemon(true);//把t1设置为守护线程
		t1.start();		int i=0;
		while(true){
			if(i++==30){
				t.chenge();//这就控制住了run方法
				t1.interrupt();
break;
				}
		       System.out.println("main---线程");}
    }
}class Th implements Runnable{
	private boolean flag=true;
	public void chenge(){
		flag=false;
	}
	/*public void run(){
		while(flag)
			System.out.println("Th---xiancheng");
	}*/
	public synchronized  void run(){
		while(flag)
			try{wait();}catch(InterruptedException d){System.out.println("异常");}
			System.out.println("Th---xiancheng");
	}}/*虽然非同步时能控制run方法,但此时主函数运行完程序就不动了,但程序并未退出,这是由于该线程一直在等待状态,没有被叫醒。所以不能判断标记。所以用interrut方法来终端冻结状态让他强制回到运行状态并且将标记置为假,线程就停下来了,如不置为假,那线程还是挂。。。如果不中断,将t1设置为守护线程也可以将程序停止*/

于是有特殊情况:

当线程处于了冻结状态。就不会读取到标记。那么线程就不会结束。当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。Thread类提供该方法 interrupt();

如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。(也就是说不是通过正常的notify从wait状态中出来,或者时间还没到,睡眠就结束。这也是上述各个方法抛InterruptedException异常的原因)

 

setDaemon(boolean);

将该线程标记为守护线程或用户线程(后台线程,和前台共同抢劫Cpu资源,其他的跟前台线程一样,但是结束时不一样,前台结束,后台就跟着结束)。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。

 

join() throws InterruptedException:等待该线程终止。

join(longmillis) throwsInterruptedException:等待该线程终止的时间最长为 millis 毫秒

join(longmillis, int nanos) throwsInterruptedException:等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。

 

当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。join可以用来临时加入线程执行。

public class T14 {
	public static void main(String[] args)throws InterruptedException {
		Thread t1=new Thread(new Th());
		Thread t2=new Thread(new Th());
		/*t1.start();
		t1.join();//此时,主线程释放执行权,等t1执行完main才开始继续执行
		t2.start();*/
		
		t1.start();
		t2.start();
		t1.join();//此时主线程释放执行权,t1和t2抢夺cpu资源,t1执行完主函数再执行
		for(int i=0;i<20;i++)
			System.out.println("main---线程");
		}
}
class Th implements Runnable{
	public  void run(){
		    for(int i=0;i<20;i++)
			System.out.println(Thread.currentThread().getName()+"--Th---xiancheng");
	}}

toString():返回该线程的字符串表示形式,包括线程名称、优先级和线程组。

static voidyield():暂停当前正在执行的线程对象,并执行其他线程。

final voidsetPriority(int newPriority):更改线程的优先级。

优先级影响抢夺cpu的频率。所有自定义的线程的优先级默认是5。

 

//匿名内部类来创建线程
public class T14 {
	public static void main(String[] args)throws InterruptedException {
		new Thread(){
			public void run(){
				for(int i=0;i<50;i++)
					System.out.println("main---线程");}}.start();
				Runnable r=new Runnable(){
			public void run(){
				for(int i=0;i<40;i++)
					System.out.println("3-------线程");}};
		new Thread(r).start();
		for(int i=0;i<20;i++)
			System.out.println("2-------线程");

		}
}









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值