23.javase-基础加强-线程一(线程状态/创建方式/方法)

线程

一.什么是进程?什么是线程?

**进程(Process)**是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

在这里插入图片描述

**线程(thread)**是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call
stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。
一个进程可以有很多线程,每条线程并行执行不同的任务。

在这里插入图片描述

CPU轮询:CPU轮询是指的在一个时间周期内,CPU会访问执行每个就绪的线程,有于CPU的计算速度非常快,所以看起来就像是多个线程同时进行.

二.线程

1.分类
系统级线程:又称核心级线程,负责管理调度不同进程之间的多个线程,由操作系统直接管理。
用户级线程:仅存于用户空间,在应用程序中控制其创建,执行和消亡。
2.守护线程
守护线程是特殊的线程,一般用于在后台为其他线程提供服务。
例如:我们启动一个杀毒软件360,然后同时在启动一个守护进程。监控360杀毒程序,如果这个程序被关闭,则自动重新启动这个程序。
3.线程死锁
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁。
在这里插入图片描述
4.线程的状态

在这里插入图片描述

三.线程的创建

1.继承thread类
注意:
1.重写其中的run()方法,这个方法没有返回值,并且不能处理异常.
2.新建线程传入我们创建的这个类的实例对象为target参数,表示执行目标对象中的run方法.
3.一般我们创建自己的线程任务类就去继承Tread,因为这个类有很多的方法,我们可以用.

public class TicketThread extends Thread{
	//这是全局变量,让所有的线程可以共享这个成员变量
	private int ticket = 30;
	@Override
	public void run() {
		//while(true)循环能快速的,让OS系统调度本线程
		while(true) {
			//加上一个同步锁,使同步锁内的代码,要么执行完,要么一行代码都不执行
			//this代表的调用本方法的对象
			synchronized (this) {
				if(ticket <= 0) {
					break;
				}
				//因为线程睡眠会出现异常,但是又不能抛出异常,所以这里我们自己捕捉异常
				try {
					//主动让线程运行慢一点,好看结果一点,再一个是线程运行速度过快
					//避免让一个线程直接抢完票了
					Thread.sleep(50);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				//输出与卖出票的窗口,顺便该窗口说一声余票还有多少张.
				System.out.println("窗口:" + Thread.currentThread().getName() 
						+ "  余票:" + ticket--);
			}
		}
	}
	public static void main(String[] args) {
		
		Thread target = new TicketThread();
		//创建一个新的线程,把我们写的线程的对象作为参数传入到新线程的构造方法中去
		//此构造方法的原理是对同一个线程做操作.及操作参数线程的所有的东西.
		//相当于将一个线程分成了3个线程来执行.
		new Thread(target).start();//新建的线程执行target目标线程的run方法
		new Thread(target).start();
		new Thread(target).start();

	}
}

2.实现Runnable接口
注意:
1.重写其中的run()方法,这个方法没有返回值,并且不能处理异常
2.我们实现runnable接口其实就是实现了线程的接口,但是我们不常用这个,因为这个相对于Thread类少了很多提供的方法.

public class TestRunnable implements Runnable{
	@Override
	public void run() {
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName());
	}
	public static void main(String[] args) {
		//调用Runnable,创建一个Runnable的对象.
		Runnable target = new TestRunnable();
		for(int i =0;i < 10 ; i ++) {
			//调用Thread的带参数构造方法,参数是Runnable的实现类.
			new Thread(target,"窗口" + (i+1)).start(); 
		}
	}
}

3.实现Callable接口
注意:
1.重写其中call()方法,这个方法可以接受返回值,和处理异常
2.如果想要获取线程的返回值,则需要用一个中间对象Future对象去包装一下目标线程对象.

//创建一个TestCallable对象实现Callable接口,类型为String类型
public class TestCallable implements Callable<String>{
	//重写call方法
	@Override
	public String call() throws Exception {
		System.out.println(Thread.currentThread().getName());
		return Thread.currentThread().getName() + "我执行完了";
	}
	
	public static void main(String[] args) throws Exception {
		//创建一个Callable的对象.
		Callable callable = new TestCallable();
		//将Callable的对象包装成FutureTask<String>的对象,
		//这个对象有个get()方法,可以获取call()方法的返回值
		FutureTask<String> ft = new FutureTask<String>(callable);
		//创建一个线程,并让它准备好.
		new Thread(ft).start();
		//调用ft的get()方法,获取返回值.
		System.out.println(ft.get());
	}
}

4.利用线程池的方式
注意:
1.创建一个线程池.
2.submit(new runnable())向线程池提交一个线程执行任务.

public class TestThreadPool {
	public static void main(String[] args) throws Exception {
		//创建一个线程池es,通过Excutors类方法,生成10个线程的线程池
		ExecutorService es = Executors.newFixedThreadPool(10);
		//运行线程池中的线程,参数是一个Runnable接口类型的参数
		//这里用匿名内部类的形式定义了一个Runnable的实现子类.
		es.submit(new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName());
			}
		});
		
		//调用Callable类型的参数,submit创建一个带有返回值的线程,该线程是Callable类型的
		//下面的这个方法的返回值是Future类型的,线程的返回值就封装在这个Future对象中
		Future<String> f = es.submit(new Callable<String>() {
			@Override
			public String call() throws Exception {
				System.out.println(Thread.currentThread().getName());
				return Thread.currentThread().getName() + "我执行完了e";
			}
		});
		//调用Future对象的get()方法,获取Callable线程Call()方法的返回值.
		System.out.println(f.get());
		//关闭线程池
		es.shutdown();
	}
}

四.线程的方法

1.Sleep()
阻塞线程,但是不会释放线程锁
2.Wait()-notify()
这两个方法在Object类定义的.阻塞线程,但是放弃对象.唤醒阻塞
3.Join()
join 多线程线程,a线程join b线程,表示a线程必须得等b线程先执行完才结束.这主要是线程排序的.
4.Start()
线程启动进入就绪状态.
5.Yeild()
暂停当前方法,释放自己拥有的CPU,线程进入就绪状态。它能让当前线程由“运行状态”进入到“就绪状态”

面试问题:
1.sleep()和wait()之间的区别.
a.sleep()是Thread的方法,二wait()是Object类定义的方法.
b.sleep()不释放锁,wait()放弃锁.
2.java中为什么要把wait方法定义在Object类中而不是线程中呢?
sleep()是睡眠当前线程,wait()是对当前对象放弃锁,因为锁可能加在任何的对象上,所以sleep()方法在Object类中定义.

发布了42 篇原创文章 · 获赞 0 · 访问量 664

猜你喜欢

转载自blog.csdn.net/weixin_45449911/article/details/104510902