Java多线程(一):进程与线程的区别、线程的生命周期和线程的创建方法

 

一、进程与线程的区别

1、进程可以理解成程序的一次执行(即动态的),所以一个程序可以对应一个或多个进程(程序的多次执行),而一个进程往往包含一个或多个线程

2、每个进程有独立的地址空间,包含资源。而线程共享进程的资源,可以把线程理解为轻量的进程

3、线程是处理器调度的基本单位,进程是程序运行的实例

为什么要区分进程和线程

因为进程的执行开销比较大,把进程划分为多个线程能相对减小执行开销,特别是在多线程并发的情况下,涉及到频繁的切换时提高了效率。但由于线程共享进程的资源,一个线程在崩溃后会影响到其他线程,而地址空间独立的进程比线程健壮的多

何时使用多进程,何时使用多线程

对资源的管理和保护要求高,不限制开销和效率时,使用多进程。

要求效率高,频繁切换时,资源的保护管理要求不是很高时,使用多线程。

 

二、线程的生命周期

线程的五种基本状态

新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();

就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;

运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就绪状态是进入到运行状态的唯一入口;

阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:

1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;

2.同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;

3.其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

死亡状态(Dead):线程执行完正常死亡,或者因为未补货到的异常猝死,退出了run()方法,该线程结束生命周期。

有必要提一下,CPU选择就绪状态线程进入运行状态时具有随机性(虽然会有优先级之分),处于就绪状态的线程会被随机切换,也就是多线程实现的原理,但CPU处理速度快,时间差很小,感觉起来就是同时执行的。这里又产生了一个新问题:在多线程只是CPU快速的切换线程,并不是真正意义上的同时执行,所以单位时间内的执行效率并没有提高,为什么说多线程并发提高了效率(针对单个CPU)?

因为多线程并发节省的不是处理时间,而是等待时间。通过线程开销小切换快的特点,能有不同的执行路径同时在CPU上切换并行,才是最主要的提高效率。对于多核主机,多个CPU并行效率会更高。

 

三、线程的三种创建方法

1、继承Thread重写run()方法

创建一个MyThrea类,通过继承Thread类,并重写其中的run()方法

public class ThreadTest {
    /*
    *继承Thread重写run()
    *
    **/
    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
			System.out.println(Thread.currentThread().getName() + " " + i);	//获取当前线程名
			if(i == 30) {
				Thread mythread1 = new MyThread();		//创建一个新线程
				
				Thread myThread2 = new MyThread();
				
				mythread1.start();					//线程进入就绪状态
				
				myThread2.start();
			}
		}
	}
}

class MyThread extends Thread{
	private int i = 0;

	@Override
	public void run() {
		
		System.out.println("in MyThread run");
		
		for(i=0; i<100; i++) {
			System.out.println(Thread.currentThread().getName() + " " + i);
		}
	}
}

 

2、实现Runnable接口并重写run()方法

这里有个问题,Thread类和Runnable接口的run()有什么关联吗,我们来看一下源码

public
class Thread implements Runnable{
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

    ...
}

public interface Runnable {
    
    public abstract void run();
}

可以看到Runnable接口中的run方法是一个抽象方法(抽象方法需要被实现),而Thread实现了Runnable接口,但却把run方法的实现交给了target对象。所以通过创建Runnable实例对象也可以创建Thread

public class ThreadTest {

    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
			System.out.println(Thread.currentThread().getName() + " " + i);
			if(i == 30) {
				Runnable myRunnable = new MyRunnable();	// 创建一个Runnable实现类的对象
				
				Thread thread1 = new Thread(myRunnable);
														// 将myRunnable作为Thread target创建新的线程
				Thread thread2 = new Thread(myRunnable);
				
				thread1.start();
				
				thread2.start();
			}
		}
    }
}

class MyRunnable implements Runnable{
	private int i = 0;
	
	@Override
	public void run() {
		
		System.out.println("in MyRunnalble run");
		
		// TODO Auto-generated method stub
		for(i=0; i<100; i++) {
			System.out.println(Thread.currentThread().getName() + " " + i);
		}
	}
	
}

Runnable实现类也可以是匿名内部类,使用匿名内部类也可以创建线程

public class ThreadTest {

    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
			System.out.println(Thread.currentThread().getName() + " " + i);
			if(i == 30) {
				new Thread( new Runnable(){					//匿名内部类,实现Runnable类的run方法
					@Override
					public void run() {
						// TODO Auto-generated method stub
					}
				}
				).start();
				
			}
		}
    }
}

 

3、使用Callable和Future接口创建线程。具体是创建Callable接口的实现类,并实现clall()方法。并使用FutureTask类来包装Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建线程。

public class ThreadTest {

    public static void main(String[] args) {

        Callable<Integer> myCallable = new MyCallable();	// 创建MyCallable对象
		
		FutureTask<Integer> ft = new FutureTask<Integer>(myCallable);	//使用FutureTask来包装MyCallable对象
		
		for(int i=0; i<100; i++) {
			System.out.println(Thread.currentThread().getName() + " " + i);
			if(i == 30) {
				Thread thread = new Thread(ft);
				
				thread.start();
			}
		}
		
		System.out.println("主线程循环for循环执行完毕...");

		try {
			int sum = ft.get();
			System.out.println("sum = " + sum);
		} catch (InterruptedException e) {			//中端异常处理
			e.printStackTrace();
			
		}catch (ExecutionException e) {				//线程执行异常处理
			e.printStackTrace();
			
		}catch(RejectedExecutionException e) {  	//提交线程池被拒绝异常处理
			e.printStackTrace();
		}

    }
}


class MyCallable implements Callable<Integer> {

	private int i = 0;
	
		//call方法与run方法不同,有返回值
	@Override
	public Integer call() throws Exception {
		// TODO Auto-generated method stub
		int sum = 0;
		for(; i<100; i++) {
			System.out.println(Thread.currentThread().getName() + " " + i);
			sum += i;
		}
		return sum;
	}
}

在Callable中的cell方法与run方法类似,但是cell方法具有返回值,这是run方法不具备的,因此使用cell方法创建的线程,还能够获取线程的返回值。而FutureTask类同时具有Runnable和Future接口的属性,既解决了线程的创建,有解决了返回值冲突的问题。

public class FutureTask<V> implements RunnableFuture<V>{
    ...
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

笔者是个小菜鸡,若有错误欢迎纠正,希望能多获得大家的建议,谢谢!

参考:https://www.cnblogs.com/lwbqqyumidi/p/3804883.html

           ↑↑↑深受这个博客的影响,有兴趣的可以去理解消化↑↑↑

           https://www.cnblogs.com/zhuzhu2016/p/5804875.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值