Java 线程详解

1 概念
进程:是操作系统进行资源分配和调度的基本单位,每个进程都有自己独立的内存空间,程序之间的切换开销比较大。
线程:是程序执行时的最小单位,它是进程的一个执行流,是cpu调度和分配的基本单位,一个进程中可以并发多个线程,每条线程并行执行多个任务。
并发:是指两个或多个事件在同一时间间隔内发生。
并行:是指两个或多个事件在同一时刻发生。

2 线程的生命周期
线程是一个动态执行的过程,它有一个从创建到销毁的过程。这个过程要经历新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)、死亡(Dead)五种状态
在这里插入图片描述
1)新建状态(New)
**使用new关键字和Thread类或其子类创建一个线程后,该线程就处于新建状态,一直保持这个状态直到调用start()。**此时线程的情况:jVM为其分配内存,并初始化成员变量的值。
2)就绪状态(Runnable)
当线程对象调用了start()方法之后,该线程就处于了就绪状态。此时线程处于线程就绪队列中,等待系统为其分配cpu时间片段。
3)运行状态(Running)
当处于就绪状态的线程获取CPU时间片段,执行run()方法,现存就处于运行状态。处于运行状态的线程可以转变成就绪状态、阻塞状态和死亡状态 。
4)阻塞状态
在线程运行过程中,由于某种原因让出CPU时间片段,该线程就进入了阻塞状态。
当发生如下情况时,线程会进入阻塞状态。
1.线程调用sleep()方法,主动释放所占用的CPU,进入中断状态(不会释放持有的对象锁),当休眠时间到达后,线程会进入就绪状态,等待获取CPU时间片段进入运行状态。
2.线程调用wait()方法,会处于阻塞状态,等待调用notify()或notifyAll()方法,唤醒线程,进入就绪状态。(会释放所持有的对象锁)
3.线程调用了suspend()方法,线程会被挂起,等待调用resume()方法,让线程进入就绪状态。
4.调用一个阻塞式IO方法,在该方法返回之前,该线程处于阻塞状态。
5.线程试图获得一个同步锁,但该同步锁正被其他线程所占据。
阻塞状态可分为三种类型:
① 等待阻塞:运行状态中的线程执行wait()方法,使线程进入等待阻塞状态;
②同步阻塞:线程在 获取synchronized同步锁失败(因为锁被其它线程占用),使线程进入同步阻塞状态;
③其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
5)死亡状态(Dead)
如果线程执行以下方式,线程就会处于死亡状态:
1.run()或call()方法执行完毕,线程正常结束。
2.调用线程的stop()方法结束该线程。
3.抛出一个未捕获的Exception或error.
线程执行完毕后就进入了终止状态。
3 线程的优先级
每个线程都有一个优先级,优先级高的线程获取CPU的概率较大。线程的优先级是一个整数,范围是 1(Thread.MIN_PRIORITY) ~ 10(Thread.MAX_PRIORITY ),默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。设置和获取优先级的方法:setPriority(int priority),getPriority().
4 线程常用到的方法
1)start() 和run()。
通过调用start()方法启动线程,线程会执行run()方法体。
调用start()方法来启动线程,系统会把run()方法当成线程执行体来执行;如果直接调用线程的run()方法,run()方法会立刻被执行,而在方法返回之前其他线程无法并发执行。
2)sleep()
通过sleep(long time)让线程休眠一段时间,进入阻塞状态,在指定的时间内没法将其唤醒,不会释放所持有的对象锁,等休眠时间到达后,进入就绪状态。
3)yield()
通过调用yield()方法,可以让当前正在执行的线程中断,让出CPU给其他线程,它不会进入阻塞状态,而是直接进入就绪状态。注意:yield()方法只让步比它优先级高的线程。
sleep()和yield()的区别:
1.sleep()暂停当前线程后,会进入阻塞状态,只有等休眠时间到达后,才会进入就绪状态;而yield()方法执行后,会直接进入就绪状态,可能会当进入就绪状态就被执行,进入运行状态。
2.sleep()方法声明抛出了InterruptedException,所以调用sleep()方法是要抛出或捕获该异常;而yield()没有声明抛出异常。
3.sleep方法比yield方法有更好的可移植性,通常不要依靠yield方法来控制并发线程的执行。
4) join()
join方法其实就是阻塞当前调用它的线程,等待join执行完毕,当前线程继续执行。
1.void join() 当前线程加入该线程后面,等待该线程执行完毕后继续执行。
2.void join(long time) 当前线程等待该线程终止的时间最长为 time 毫秒。 如果在time时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待cpu调度 .
3.void join(long time1,int time2) 等待该线程终止的时间最长为 time1毫秒 + time2纳秒。如果在time1时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待cpu调度.

package com.test.test1;
public class Test1 {
	public static void main(String[] args) throws InterruptedException{
		MyThread my1 = new MyThread();	
		my1.start();  
		my1.join(1);	
		for(int i=0;i<30;i++){  
            System.out.println(Thread.currentThread().getName() + "线程第" + i + "次执行!");  
        }  
	}	
}
class MyThread extends Thread{
	public void run(){
	 for (int i = 0; i < 200; i++) {  
            System.out.println(this.getName() + "线程第" + i + "次执行!");  
        }  
	}
}

5)wait(),notify(),notifyAll()
wait,notify,notifyAll这三个都是Object类的方法。使用 wait ,notify 和 notifyAll 前提是先获得调用对象的锁,一般在synchronized 同步代码块里使用。
1.调用 wait 方法后,释放持有的对象锁,线程状态有运行变为阻塞状态,并将当前线程放置到对象的 等待池中,等待池中的线程不会去竞争该对象的锁;
2.当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。
notify()和notityAll()区别:
notify 将等待队列的一个等待线程从等待池移到锁池中 ,而 notifyAll 将等待池中的所有的线程全部移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。而notify只会唤醒一个线程。
例:先打印A-1,在打印B-1,B-2,再打印A-2,如此返回。

package com.smart.web;
public class Test {
	public static void main(String[] args) {
		Object lock = new Object();
		Thread t1 = new Thread(new ThreadA(lock));
		Thread t2 = new Thread(new ThreadB(lock));
		t1.start();
		t2.start();
	}
}
class ThreadA implements Runnable {
	private Object lock;
	public ThreadA(Object lock) {
		this.lock = lock;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while (true) {
			System.out.println("A等待锁!");
			synchronized (lock) {
				System.out.println("A获得锁!");
				System.out.println("A-1");
				try {
					System.out.println("A放弃锁!");
					lock.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("A重新获得锁");
				System.out.println("A-2");
				lock.notifyAll();
			}
		}
	}
}
class ThreadB implements Runnable {
	private Object lock;
	public ThreadB(Object lock) {
		this.lock = lock;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while (true) {
			System.out.println("B等待锁!");
			synchronized (lock) {
				System.out.println("B得到锁!");
				System.out.println("B-1");
				System.out.println("B-2");
				System.out.println("调用notify,唤醒A");
				lock.notifyAll();
				try {
					System.out.println("B放弃锁!");
					lock.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

}

运行结果:

A等待锁!
A获得锁!
A-1
A放弃锁!
B等待锁!
B得到锁!
B-1
B-2
调用notify,唤醒A
B放弃锁!
A重新获得锁
A-2
A等待锁!
A获得锁!
A-1
A放弃锁!
B等待锁!
B得到锁!
B-1
B-2
调用notify,唤醒A
B放弃锁!
A重新获得锁
A-2

生产消费者模式(wait(),notifyall(),notify实现)

package com.collect.thread;
//先定义一个仓库接口
public interface AbstractStock {
	//定义一个生产的方法
	public void product(int num);
	//定义一个消费的方法
	public void consume(int num);
}

package com.collect.thread;

import java.util.LinkedList;
import java.util.List;
//实现仓库接口
public class Stock implements AbstractStock{
	private final int MAX_SIZE = 50;//最大库存容量
	private List<Object> list = new LinkedList<Object>();//仓库载体
	
	//生产产品
	@Override
	public void product(int num) {
		// TODO Auto-generated method stub
		synchronized(list){
			while(list.size()+num >MAX_SIZE){
				System.out.println("库存数为:"+list.size()+"--,要生产:"+num);
				try {
					System.out.println("库存已满,停止生产");
					list.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			for(int i=0;i<num;i++){
				list.add(new Object());
			}
			 System.out.println("已经生产产品数:" + num + "库存为:" + list.size());
			list.notifyAll();
		} 
	}
	//消费产品
	@Override
	public void consume(int num) {
		// TODO Auto-generated method stub
		synchronized(list){
			while(num>list.size()){
				System.out.println("库存数为:"+list.size()+"--,要消费数为:"+num);
				try {
					System.out.println("库存不够,停止消费");
					list.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			for(int i=0;i<num;i++){
				list.remove(i);
			}
			System.out.println("消费数为:"+num+";剩余库存:"+list.size());
			System.out.println("唤醒生产者");
			list.notifyAll();
		}
	}

}

package com.collect.thread;

public class ThreadTest{
	public static void main(String[] args){
		AbstractStock abs= new Stock();
		Thread pt1 = new Thread(new Productor(abs,10));
		Thread pt2 = new Thread(new Productor(abs,20));
		Thread pt3 = new Thread(new Productor(abs,30));
		Thread con1 = new Thread(new Consumer(abs,5));
		Thread con2 = new Thread(new Consumer(abs,6));
		pt1.start();
		pt2.start();
		pt3.start();
		con1.start();
		con2.start();
		
	}
	
	
}
//生产者
class Productor  implements Runnable{
	private int num;//每次生产数量
	private AbstractStock abstractStock;//仓库
	public Productor(AbstractStock abstractStock,int num){
		this.abstractStock = abstractStock;
		this.num = num;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		abstractStock.product(num);
	}
}
//消费者
class Consumer implements Runnable{
	private int num;//每次消费数量
	private AbstractStock abstractStock;//仓库
	public Consumer(AbstractStock abstractStock,int num){
		this.abstractStock = abstractStock;
		this.num = num;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		abstractStock.consume(num);
	}
	
}

6) suspend(), resume () (已过时)
调用suspend()方法,线程会进入阻塞状态,但不会释放锁,会造成死锁的问题。
resume()使线程重新进入可执行状态。
7) stop() (已过时)
当有线程调用时,停止该线程的运行。现场不安全,容易造成死锁,现已过时。
8)boolean isAlive() 判断当前线程是否处于活跃状态。
9)void interrupt() 不能中断正在运行的线程,只能改变中断的状态。
boolean isInterrupted() 判断当前线程的中断状态,true 表示中断。
boolean interrupted() 用来恢复中断状态。

package com.aem.sheep.test;
public class test {
	public static void main(String[] args) throws InterruptedException {
		Thread th = new Thread(new MyThread());
		th.start();
		th.interrupt();
		for (int i = 0; i < 10; i++) {
			System.out.println("main-" + i);
		}
	}
}
class MyThread implements Runnable {
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for (int i = 0; i < 50; i++) {
			if (i == 10) {
				Thread.currentThread().interrupted();
			}
			if (Thread.currentThread().isInterrupted()) {
				System.out.println("Yes,I am interruted-" + i);
			} else {
				System.out.println("not yet interrupted-" + i);
			}
		}
	}
}

10)void setDaemon(boolean on) 把该线程标记为守护线程或是用户线程。
守护线程:一般后台运行的线程,比如JVM的垃圾回收、内存管理等线程,都称为守护线程。
直接调用setDaemon(true)可把线程变为守护线程。该方法必须在线程启动之前设置。JRE判断程序是否执行结束的标准是所有的前台执线程行完毕了,而不管后台线程的状态。

5 创建一个线程
创建线程的三种方式:继承Thread类,实现Runnable接口,通过callable和Future创建线程。

1)继承Thread类。需要创建一个新类,该类要继承Thread类,需要重写run()方法。

package com.smart.thread;
public class Test2 {
	public static void main(String[] args) {
		Thread t1 = new Thread(new MyThread("A"));
		Thread t2 = new Thread(new MyThread("B"));
		t1.start();
		t2.start();
	}
}
class MyThread extends Thread {
	private String name;
	public MyThread(String name) {
		this.name = name;
	}
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(this.getName() + "-" + i);
		}
	}
}

2)实现Runnable接口。需要创建一个类实现Runnable接口,重写run()方法.
3)通过Callable和Future创建线程
1.创建一个类实现Callable接口,并实现call()方法,这个call()方法作为执行体,相当于run()方法,有返回值;
2.创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
3. 使用 FutureTask 对象作为 Thread 对象的目标创建并启动新线程。
4. 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

package com.collect.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableTest implements Callable<Integer>{
	public static void main(String[] args){
		CallableTest ca = new CallableTest();
		FutureTask<Integer> ft = new FutureTask<Integer>(ca);
		new Thread(ft).start();
		try {
			System.out.println("返回数:"+ft.get());
		} catch (InterruptedException | ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public Integer call()  {
		// TODO Auto-generated method stub
		int i = 0;
		for(;i<5;i++){
			System.out.println(Thread.currentThread().getName()+"-"+i);
		}
		return i;
	}

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值