多线程常用方法总结

本文总结了Java多线程中的线程命名与取得、sleep()、yield()、join()方法,线程停止的方式,线程优先级,以及守护线程的概念。详细介绍了每个方法的使用和注意事项,例如sleep()不会释放锁,yield()让当前线程交出CPU执行权,join()用于等待线程结束,以及如何安全地停止线程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线程的命名与取得

多线程的运行状态是不确定的,所以对于多线程操作必须有一个明确标识出线程对象的信息,这个信息往往通过名 称来描述。在Thread类中提供有如下的线程名称方法:

NO方法名称类型描述
1public Thread(Runnable target, String name)构造创建线程时设置名称
2public final synchronized void setName(String name)普通设置线程名称
3public String getName()普通取得线程名称

要想取得线程的对象,在Thread类中提供有一个方法取得当前线程对象:

/**     
* Returns a reference to the currently executing thread object.          
* @return  the currently executing thread.     
*/    
public static native Thread currentThread();
范例:观察取得线程名称
class MyThread implements Runnable {
 
    @Override    
    public void run() {        
    	for (int i = 0; i < 10 ; i++) {            
    	System.out.println("当前线程:"+Thread.currentThread().getName()+" ,i = "+i);        
    	}    
    }    
} 
    
public class TestDemo {    
    public static void main(String[] args) {        
    	MyThread mt = new MyThread() ;         
    	new Thread(mt).start(); // 没有设置名字        
    	new Thread(mt).start(); // 没有设置名字        
    	new Thread(mt,"yuisama").start(); // 有设置名字    
   	}
}

通过上述代码发现,如果没有设置线程名字,则会自动分配一个线程名字。需要主要的是,线程名字如果要设置请 避免重复,同时中间不要修改。

范例:观察线程的执行结果
class MyThread implements Runnable {    
	@Override
	public void run() { 
		 System.out.println("当前线程:" + Thread.currentThread().getName());    			 	
	}
}

public class TestDemo {    
	public static void main(String[] args) {        
	MyThread mt = new MyThread();        
	mt.run(); // 直接通过对象调用run()方法        
	new Thread(mt).start(); // 通过线程调用    
	} 
}

通过以上程序我们发现,主方法本身就是一个线程,所有的线程都是通过主线程创建并启动的。

问题:进程在哪?

实际上每当使用了java命令去解释程序的时候,都表示启动了一个新的JVM进程。而主方法只是这个进程上的一个 线程而已。
在讲线程的其他常用方法以前,先来看张图:
在这里插入图片描述

线程休眠 sleep()方法

线程休眠:指的是让线程暂缓执行一下,等到了预计时间之后再恢复执行。
线程休眠会交出CPU,让CPU去执行其他的任务。但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当 前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象。
方法:

public static native void sleep(long millis) throws InterruptedException

休眠时间使用毫秒作为单位。

范例:处理休眠操作
class MyThread implements Runnable {    
	@Override    
	public void run() {        
		for (int i = 0; i < 1000 ; i++) {            
			try {                
				Thread.sleep(1000);            
			} 
			catch (InterruptedException e) {                
				// TODO Auto-generated catch block                
				e.printStackTrace();            
			}            
			System.out.println("当前线程:" + 				Thread.currentThread().getName()+" ,i = " +i);        
		}    
	} 
}
 
public class TestDemo {    
	public static void main(String[] args) {        
		MyThread mt = new MyThread();        
		new Thread(mt).start();         
		new Thread(mt).start();         
		new Thread(mt).start();     
	}
}

通过代码观察会错误的认为这三个线程是同时休眠的,但是千万要记住,所有的代码是依次进入到run()方法中的。
真正进入到方法的对象可能是多个,也可能是一个。进入代码的顺序可能有差异,但是总体的执行是并发执行。

线程让步 yield()方法

暂停当前正在执行的线程对象,并执行其他线程。
——意思就是调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放 锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间 的机会。
注意: 调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时 间,这一点是和sleep方法不一样的。

范例:观察yield方法
class MyThread implements Runnable {    
	@Override    
	public void run() {        
		for (int i = 0; i < 3 ; i++) {
			Thread.yield();            
			System.out.println("当前线程:" + Thread.currentThread().getName()+" ,i = " +i);        
		}  
	} 
} 

public class Test {    
	public static void main(String[] args){        
	MyThread mt = new MyThread();        
	new Thread(mt).start();        
	new Thread(mt).start();        
	new Thread(mt).start();    
	} 
}

join()方法

等待该线程终止。意思就是如果在主线程中调用该方法时就会让主线程休眠,让调用该方法的线程run方法先执行 完毕之后在开始执行主线程。

范例:观察join()方法
class MyThread implements Runnable {    
	@Override    
	public void run() {        
		try {            
			System.out.println("主线程睡眠前的时间");    
			Test.printTime(); 			           
			Thread.sleep(2000);
			System.out.println(Thread.currentThread().getName());  
			System.out.println("睡眠结束的时间");             			                   
			Test.printTime();        
			} catch (InterruptedException e) {            
				// TODO Auto-generated catch block   
				e.printStackTrace();        
			}            
	} 
} 

public class Test {    
	public static void main(String[] args) throws InterruptedException{   
	     MyThread mt = new MyThread();        
	     Thread  thread = new Thread(mt,"子线程A");        
	     thread.start();
	     System.out.println(Thread.currentThread().getName());
	     thread.join();        
	     System.out.println("代码结束");    
	     }    
	public static void printTime() {           
		Date date=new Date();           
		DateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
		String time=format.format(date);
		System.out.println(time);        
	} 
}

线程停止

多线程中有三种方式可以停止线程。

  1. 设置标记位,可以是线程正常退出。
  2. 使用stop方法强制使线程退出,但是该方法不太安全所以已经被废弃了。
  3. 使用Thread类中的一个interrupt() 可以中断线程。
范例:设置标记为使线程退出
class MyThread implements Runnable {    
	private boolean flag = true;    
	@Override    
	public void run() {        
		int i = 1;        
		while (flag) {            
			try {                
				Thread.sleep(1000);
				System.out.println("第"+i+"次执行,线程名称为:"+Thread.currentThread().getName());                
				i++;            
			} catch (InterruptedException e) {                
				// TODO Auto-generated catch block
				e.printStackTrace();            
			}                    
		}    
	}    
	public void setFlag(boolean flag) {        
		this.flag = flag;    
	} 
} 

public class Test {    
	public static void main(String[] args) throws InterruptedException {
		MyThread myThread = new MyThread();        
		Thread thread1 = new Thread(myThread,"子线程A");
		thread1.start();        
		Thread.sleep(2000);        
		myThread.setFlag(false);        
		System.out.println("代码结束");    
	}
}
范例:使用stop方法使线程退出
 MyThread myThread = new MyThread();        
 Thread thread1 = new Thread(myThread,"子线程A");        
 thread1.start();        
 Thread.sleep(3000);        
 thread1.stop();        
 System.out.println("代码结束");

使用stop方法强制使线程退出,但是该方法不太安全所以已经被废弃了。

为什么说不安全呢?因为stop会解除由线程获取的所有锁定,当在一个线程对象上调用stop()方法时,这个线程对象 所运行的线程就会立即停止,假如一个线程正在执行:synchronized void { x = 3; y = 4;} 由于方法是同步的,多个 线程访问时总能保证x,y被同时赋值,而如果一个线程正在执行到x = 3;时,被调用了 stop()方法,即使在同步块中, 它也会马上stop了,这样就产生了不完整的残废数据。

范例:使用Thread.interrupt()
class MyThread implements Runnable {    
	private boolean flag = true;    
	@Override   
	public void run() {        
		int i = 1;        
		while (flag) {            
			try {                 
			/**                    
			* 这里阻塞之后,线程被调用了interrupte()方法,                     
			* 清除中断标志,就会抛出一个异常                    
			* java.lang.InterruptedException                    
			*/                
				Thread.sleep(1000);                
				boolean bool = Thread.currentThread().isInterrupted();                
				if (bool) {                       
					System.out.println("非阻塞情况下执行该操作。。。线程状态" + 			bool);                       break;                
				}                
				System.out.println("第"+i+"次执行,线程名称	为:"+Thread.currentThread().getName());                
				i++;            
			} catch (InterruptedException e) {                
				System.out.println("退出了");                   
				/**                    
				* 这里退出阻塞状态,且中断标志被系统会自动清除,                    
				* 并且重新设置为false,所以此处bool为false                    
				*/                   
				boolean bool = Thread.currentThread().isInterrupted();                   			
				System.out.println(bool);                   
				//退出run方法,中断进程                   
				return;            
			}               
		}    
	}   
 
	public void setFlag(boolean flag) {        
		this.flag = flag;    
	} 
} 

public class Test {    
	public static void main(String[] args) throws InterruptedException {        		
		MyThread myThread = new MyThread();        
		Thread thread1 = new Thread(myThread,"子线程A");        
		thread1.start();        
		Thread.sleep(3000);        
		thread1.interrupt();        
		System.out.println("代码结束");    
}
interrupt() 方法只是改变中断状态而已,它不会中断一个正在运行的线程。这一方法实际完成的是,给受阻塞的线 程发出一个中断信号,这样受阻线程就得以退出阻塞的状态。


然而interrupte()方法并不会立即执行中断操作;具体而言,这个方法只会给线程设置一个为true的中断标志(中断标 志只是一个布尔类型的变量),
而设置之后,则根据线程当前的状态进行不同的后续操作。如果,线程的当前状态 处于非阻塞状态,那么仅仅是线程的中断标志被修改为true而已;
如果线程的当前状态处于阻塞状态,那么在将中 断标志设置为true后,还会有如下三种情况之一的操作:

如果是wait、sleep以及jion三个方法引起的阻塞,那么会将线程的中断标志重新设置为false,并抛出一个 InterruptedException;

如果在中断时,线程正处于非阻塞状态,则将中断标志修改为true,而在此基础上,一旦进入阻塞状态,则按照阻塞 状态的情况来进行处理;
例如,一个线程在运行状态中,其中断标志被设置为true之后,一旦线程调用了wait、 jion、sleep方法中的一种,立马抛出一个InterruptedException,
且中断标志被程序会自动清除,重新设置为false。

通过上面的分析,我们可以总结,调用线程类的interrupted方法,其本质只是设置该线程的中断标志,将中断标志 设置为true,
并根据线程状态决定是否抛出异常。因此,通过interrupted方法真正实现线程的中断原理是:开发人员 根据中断标志的具体值,来决定如何退出线程。

线程优先级

线程的优先级指的是,线程的优先级越高越有可能先执行,但仅仅是有可能而已。
在Thread类中提供有如下优先级方法:

  • 设置优先级:
public final void setPriority(int newPriority)

  • 取得优先级:
public final int getPriority()

对于优先级设置的内容可以通过Thread类的几个常量来决定:

  1. 高优先级:public final static int MAX_PRIORITY = 10;
  2. 中等优先级:public final static int NORM_PRIORITY = 5;
  3. 低优先级:public final static int MIN_PRIORITY = 1;
范例:设置优先级
class MyThread implements Runnable {   
	@Override    
	public void run() {
		for (int i = 0; i < 5 ; i++) {            
			System.out.println("当前线程:" + Thread.currentThread().getName()+" ,i = " +i);        
		}   
	} 
}
 
public class TestDemo {    
	public static void main(String[] args) {        
		MyThread mt = new MyThread();        
		Thread t1 = new Thread(mt,"1");         
		Thread t2 = new Thread(mt,"2");        
		Thread t3 = new Thread(mt,"3");        	
		t1.setPriority(Thread.MIN_PRIORITY);        
		t2.setPriority(Thread.MIN_PRIORITY);        
		t3.setPriority(Thread.MAX_PRIORITY);        
		t1.start();        
		t2.start();        
		t3.start();    
	} 
}

主方法是一个线程,那么主线程的优先级是什么呢?

public class TestDemo {    
	public static void main(String[] args) {        	
		System.out.println(Thread.currentThread().getPriority());    
	} 
}

主方法只是一个中等优先级
线程具有继承性
线程是有继承关系的,比如当A线程中启动B线程,那么B和A的优先级将是一样的。

范例:观察线程继承性
class A implements Runnable {    
	@Override    
	public void run() {        
		System.out.println("A的优先级为:" + Thread.currentThread().getPriority());        
		Thread thread = new Thread(new B());        
		thread.start();    
	}
} 

class B implements Runnable {    
	@Override    
	public void run() {        
	System.out.println("B的优先级为:" + Thread.currentThread().getPriority());    
	}
}
 
public class Test {
	public static void main(String[] args) throws InterruptedException {        	
		Thread thread = new Thread(new A());        		
		thread.setPriority(Thread.MAX_PRIORITY);        
		thread.start();    
	} 
}

守护线程

守护线程是一种特殊的线程, 它属于是一种陪伴线程。简单点说 java 中有两种线程:用户线程和守护线程。可以通 过isDaemon()方法来区别它们:如果返回false,则说明该线程是“用户线程”;否则就是“守护线程”。典型的守护线 程就是垃圾回收线程。只要当前JVM进程中存在任何一个非守护线程没有结束,守护线程就在工作;只有当后一 个非守护线程结束时,守护线程才会随着JVM一同停止工作。
**注意:**主线程main是用户线程。

范例:观察守护线程
class A implements Runnable {    
	private int i;
 	@Override    
 	public void run() {        
 		try {            
 			while (true) {                
 				i++;                
 				System.out.println("线程名称:" + Thread.currentThread().getName() + ",i=" + i + ",是否为守护线程:"                        + Thread.currentThread().isDaemon());                
 				Thread.sleep(1000);            
 			}        
 		} catch (InterruptedException e) {            
 			System.out.println("线程名称:" + Thread.currentThread().getName() + "中断线程了");        
 		}    
 	} 
 }
 
public class Test {    
	public static void main(String[] args) throws InterruptedException {        	
		Thread thread1 = new Thread(new A(),"子线程A");        
		// 设置线程A为守护线程,此语句必须在start方法之前执行        
		thread1.setDaemon(true);        
		thread1.start();                
		Thread thread2 = new Thread(new A(),"子线程B");        
		thread2.start();        
		Thread.sleep(3000);        
		// 中断非守护线程        
		thread2.interrupt();        
		Thread.sleep(10000);        
		System.out.println("代码结束");    
	} 
}

从上面的代码可以看出来,B是用户线程当它中断了之后守护线程还没有结束,是因为主线程(用户线程)还没有 结束,所以说明是所有的用户线程结束之后守护线程才会结束。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值