Java多线程详解

本文详细介绍了Java虚拟机启动、线程创建、管理、生命周期及关键方法,包括线程池、后台线程设置、join方法、yield与sleep的区别、异常处理、同步机制、wait与notify方法的应用,以及避免死锁的策略。

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

当 Java 虚拟机启动时,通常都会有单个非守护线程(它通常会调用某个指定类的 main 方法)。Java 虚拟机会继续执行线程,直到下列任一情况出现时为止:

· 调用了 Runtime 类的 exit 方法,并且安全管理器允许退出操作发生。

· 非守护线程的所有线程都已停止运行,无论是通过从对 run 方法的调用中返回,还是通过抛出一个传播到 run 方法之外的异常。

创建新执行线程有两种方法。一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例。例如,计算大于某一规定值的质数的线程可以写成:

class PrimeThread extends Thread {
         long minPrime;
         PrimeThread(long minPrime) {
             this.minPrime = minPrime;
         }
 
         public void run() {
             // compute primes larger than minPrime
              . . .
         }
     }
然后,下列代码会创建并启动一个线程:
     PrimeThread p = new PrimeThread(143);
     p.start();

创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。采用这种风格的同一个例子如下所示:

class PrimeRun implements Runnable {
         long minPrime;
         PrimeRun(long minPrime) {
             this.minPrime = minPrime;
       
         public void run() {
             // compute primes larger than minPrime
              . . .
         }
     }

然后,下列代码会创建并启动一个线程:   

//普通方法启动线程
 PrimeRun p = new PrimeRun(143);
 new Thread(p).start();
//使用线程池启动线程
PrimeRun p = new PrimeRun(143);
ExcutorService exec=Excutors.newCachedThreadpool();
Exec.execute(p);

后台线程 使用setDaemon()方法设置,后台线程不是程序不可或缺的部分,当所有非后台线程执行完毕后,程序结束并且会杀死所有的后台线程。

线程的join方法详解:

public class Test extends Thread{	 		
	public static void main(String[] args){
		Test1 t=new Test1();
		t.start();
		try{
			t.join();//先执行完t线程再执行主线程
		}catch(Exception e){
		} 
		for(int i=0;i<10;i++){
			System.out.println("test");
		}
	}
}
class Test1 extends Thread{
	public void run(){
		for(int i=0;i<10;i++){
			System.out.println("test1");
		}
	}
}

Thread.yield():让其他线程执行   ;Thread.sleep():当前线程休眠,这两个方法都不会释放对象锁。

线程的异常如果逃出了run方法,那么就会向外传播到控制台,所以我们要在run方法中捕获异常

所有对象都自动含有单一的锁,在其对象上调用任意Synchronized方法的时候,这个对象都会被加锁,这时该对象上的其他Synchronized方法只有等到前一个方法调用完毕并释放锁之后才能被调用。

一个任务可以同一个对象中的Synchronized方法,因为这个任务在第一次调用的时候已经获得了对象锁。

Synchronized修饰的方法或者同步块的时候,就锁上对象,但是这里锁仅仅对对象的其他Synchronized方法或者同步块起作用,至于那些非同步的方法,我们还是可以在加锁后继续访问:

如下面这个例子:

public class TestThread{
	public static void main(String[] args){
		Tt tt=new Tt();
		Thread thread=new Thread(tt);
		thread.start();
		try{
		Thread.sleep(1000);
		}catch(Exception e){
		}
		tt.m2();
	}
}
class Tt implements Runnable{
	public synchronized void m1(){
		for(int i=0;i<10;i++){
		try{
			Thread.sleep(1000);
		}catch(Exception e){
		}
			System.out.println("m1");
		}
	}
	public void m2(){
		System.out.println("m2");
	}
	public void run(){
		Tt tt=new Tt();
		tt.m1();
	}
}

防止任务在共享资源上产生冲突的方法;1.同步  2.根除对变量的共享--线程的本地存储

Sleep,yield不会释放锁,wait会释放锁

只能在同步控制方法或者同步控制块里面调用wait,notify,notifyAll,在非同步方法里面调用的时候可以通过编译,但是运行的时候会出现异常。

最后写了一个死锁小程序:

class Task implements Runnable{
	static Object o1=new Object(),o2=new Object();
	public int flag=0;
	public void run(){
		if(flag==1){
			synchronized(o1){
				System.out.println("flag==1-------Synchronized o1");
				try{
				Thread.sleep(1000);
				}catch(InterruptedException e){
				
				}
				synchronized(o2){
					System.out.println("flag==1-------Synchronized o2");			
				}
			}
		}
		if(flag==0){
			synchronized(o2){
				System.out.println("flag==0-------Synchronized o1");
				try{
				Thread.sleep(1000);
				}catch(InterruptedException e){	
				}
				synchronized(o1){
					System.out.println("flag==0-------Synchronized o2");			
				}
			}
		}
	}
}
public class DeadLock{
	public static void main(String[] args){
		Task task1=new Task();
		Task task2=new Task();
		task1.flag=1;
		task2.flag=0;
		Thread t1=new Thread(task1);
		Thread t2=new Thread(task2);
		t1.start();
		t2.start();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值