多线程

进程
运行中的任务通常对应一个进程。进程是处于运行中过程中的程序。并且具有一定的独立功能。进程是系统进行资源分配和调度的一个独立单位。

  • 独立性:进程是系统中独立存在的实体,拥有自己独立的资源。
  • 动态性:进程是一个正在系统中活动的指令集合,具有自己的生命周期和不同状态。
  • 并发性 :多个进程可以在处理器中并发执行,不会互相影响。

线程
线程是进程的执行单元。一个进程可以拥有多个线程,每个线程互相独立。

一、线程的创建和启动

所有的线程对象都必须是Thread类或其子类的实例。
1、通过继承Thread类来创建

  • 定义Thread类的子类,并重写run方法,run方法就是要执行的任务。
  • 创建对象
  • 调用线程的start方法启动线程。

2、通过Runnable接口创建

  • 实现Runnable接口,重写接口的run方法
  • 创建Runnable实现类的实例,作为Thread类的参数创建Thread类对象,
  • 使用Thread对象调用start方法

3、使用Callable和Future创建线程
Callable接口使用call方法作为线程执行体,call方法可以有返回值,可以声名异常,使用Future接口来代表call方法的返回值。
FutureTask实现类Future接口也实现类Runnable接口,所有可以传入Thread创建线程。

二、线程的生命周期

1、五个状态

  • 新建:分配内存阶段,new一个对象一样。
  • 就绪:调用start方法的阶段,这个时候是就绪阶段,未必能够立刻进入运行,等待线程调用度的调度。不能重复调用start方法。
  • 运行:线程活动cpu资源,开始执行线程体的时候,处于运行状态
  • 阻塞:线程未必能一直处于运行状态,中断的时候进入阻塞状态,回到就绪状态等待调度。
  • 死亡:线程体执行完,正常结束,或者抛出异常没有捕获会进入死亡状态,死亡状态的线程,不能重新调用start去启动,可用isAlive方法判断,新建、死亡的时候该方法返回false。
三、控制线程

1、join线程
join方法可以让一个线程等待另一个线程执行完毕后再执行。当在某个执行程序流中调用其他线程的join方法时,调用线程将被阻塞。

public static void main(String[] args) {
		 // 继承Thread的线程类
		 FirstThread ft = new FirstThread(); 
		for (int i = 0; i < 100; i++){
			System.out.println("当前线程:"+Thread.currentThread().getName()+i);
			if (i == 20) {
				try {
					ft.start();
					// 在main线程的执行体调用其他线程的join方法,main线程进入阻塞状态
					ft.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

2、后台线程
后台线程又称守护线程,它的任务就是为其他的线程提供服务。如果所有的前台线程都死亡,后台线程会自动死亡。

  • setDaemon(true): 把线程指定为后台线程 ,在线程启动前设置,不然会报错。
  • isDaemon(): 判断指定线程是否为后台线程。
    垃圾回收线程就是后台线程。后台线程产出的线程默认也是后台线程。

3、线程睡眠sleep
sleep方法是是thread类的静态方法,可以让线程暂停一段时间,并进入阻塞状态。

  • static void sleep(long millis):让线程暂停多少毫秒。
  • static void sleep(long millis,int nanos):毫秒再加多少微妙,比较少用到。

4、线程让步yield
yield方法也叫线程让步,也是静态方法,它可以让当前正在执行的线程暂停,但不会阻塞线程,而是进入就绪状态重新排优先级。

sleep方法与yield方法的区别

  • sleep方法暂停当前的线程后,会给其他线程执行机会,不会理会其他线程的优先级,但yield方法只会给优先级相同的或者优先级高的线程机会。
  • sleep方法会将线程进入阻塞状态,而yield方法会让线程进入就绪状态
  • sleep方法声明抛出InterruptedException异常,所以要么捕捉异常或者抛出,而yield没有声明异常抛出。
  • sleep方法比yield方法有更好的移植性,一般不建议使用yield方法

5、改变线程的优先级
优先级高的线程获得较多的执行机会。
thread类提供了setPriority(int newPriority)、getPriority()方法来设置或获取线程的优先级,参数是一个整数,范围是1~10只间。thread类也提供了3个静态常量。

  • MAX_PRIORITY :10
  • MIN_PRIORITY:1
  • NORM_PRIORITY:5

不同系统的优先级不同,所以用常量来设置,可以让程序有比较好的移植性。

四、线程同步

1、同步代码块

synchronized(obj){
}

obj是同步监视器,任何对象都可以作为同步监视器
2、同步方法
同步方法使用synchronized关键字来修饰方法。同步方法的监视器是this,对象本身。
3、释放锁的情况

  • 当前线程的同步方法,同步代码块执行结束,当前线程即释放同步监视器。
  • 遇到break、return终止了该代码块,该方法的继续执行,当前线程将会释放同步监视器。
  • 出现了error或者exception,导致异常结束时,会释放。
  • 使用wait()方法也会释放。

不会释放的情况:

  • 调用sleep、yield方法来暂停的时候,当前线程不会释放监视器。
  • 使用suspend方法也不会释放。尽量避免使用suspend()和resume()方法。

4、同步锁lock
java5后提供了Lock对象。lock是控制多个线程对共享资源进行访问的工具,每次只能有一个线程对lock对象进行加锁,线程开始访问共享资源前应先获得lock对象。
使用中,比较常用实现类ReentranLock。格式:

class x{
	private final ReentrantLock lock = new ReentrantLock();
	public void m(){
		//加锁
		lock.lock();
		try{
			//...
		}
		finally{
			lock.unlock();
		}
	}
}

5、死锁
两个线程相互等待对方释放同步监视器时就会发生死锁。

五、线程的通信

使用Executors工厂类产生线程池。

  • newCachedThreadPool():创建一个具有缓存功能的线程池,系统根据需要创建线程,这些线程将会缓存在线程池中。
  • newFixedThreadPool(int nThreads):创建一个可重用的、具有固定线程数的线程池。
  • newSingleThreadExecutor():创建只有一个线程的线程池。
  • newScheduledThreadPool(int corePoolSize):创建具有指定线程数的线程池,它可以在指定延迟后执行线程任务。
  • newSingleThreadScheduleExecutor():创建只有一个线程的线程池,可以在指定延迟后执行线程任务。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值