一、基础知识
1.定义
-
进程
简单来说,进程就是指正在运行的程序。也就是说,当一个程序进入内存运行,就变成了一个进程。进程中包含一个或多个线程。
单进程中的单线程:main方法从入口到结束,一条路走到底。目前对初学者来说所学的Java基础只涉及单进程中的单线程和单进程中的多线程。 -
线程
线程是一个进程中的执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个程序也可以称为多线程程序。
2.线程和进程的区别
①一个进程至少有一个线程,线程的划分尺度小于进程,是进程中的一个执行单元。
②进程应有独立的内存单元,而多个线程共享这些内存单元,从而极大地提高了程序的运行效率。
③进程可以独立运行,但线程必须依附于进程执行,不能独立运行。
3.原理
进程和线程是并发运行的,简称“并发”,不是同时运行(并行)的。
并发:在同一时间间隔内发生
并行:同一时间发生
4.线程状态
1.就绪状态:New线程后,该线程具有可运行的资格,等待进入运行状态。
2.执行状态:指运行状态,线程调用start()方法后,进入正在运行状态。
3.阻塞状态:当线程等待后,可能会成为受阻塞状态。
4.新建状态:New线程
5.结束状态:线程运行完等待回收。
5.创建线程的方法及原理
创建线程共有三种方法。
1、继承Thread类
步骤:
①定义一个类继承Thread类
②重写run()方法
③主函数中创建子类对象就是创建线程对象
④调用start()方法,开启线程并让线程执行,并告诉JVM调用run()方法
2、实现Runnable接口的类
步骤:
①定义一个类实现Runnable接口
②覆盖接口中的run()方法
③创建Thread类的对象
④将Runnable接口的子类对象作为参数传给Thread类的构造方法
⑤调用Thread类的start()方法开线程
3、实现Callable接口(泛型)有返回值
步骤:
①工厂类Executors静态方法newFixedThreadPool,来创建线程池对象
②线程池对象ExecutorService接口实现类,调用submit提交线程任务ExecutorService
示例:
ExecutorService ex = Executors.newFixedThreadPool(2);
Future submit = ex.submit(new CallableDemo());
String s=submit.get();//要抛出异常
System.out.println(s);
二、线程安全
1.定义
线程休眠后,可能会进入执行状态,也可能会进入受阻塞状态,此时线程就会出现安全隐患。
2.同步技术
为了解决线程安全问题,我们通常采用同步技术。
3.三种同步技术
①同步代码块
synchronized (任意对象){
线程要操作的共享数据
}//同步代码块
对象:同步锁
作用:保证安全,没有锁的线程不能执行
注意:线程在遇到同步代码块后,线程会判断同步锁还有没有,如果有,获取锁进入同步中,去执行代码,执行完毕后,除去同步代码块,线程再将锁还回去。没有锁的线程,不能进入同步。在同步中的线程不出去同步,不会释放锁。
②同步方法
public synchronized void med(){
共享数据
}
注意:同步方法也是有锁的,是本类对象引用(this)
③静态同步方法
public static synchronized void med(){
共享数据
}
注意:静态同步方法也有锁,但不是this,静态对象生命周期早,应是类名.class
4.死锁
当线程任务中出现了多个同步时候,如果同步中嵌套了其他同步,这时候容易一种现象,程序会出现无限等待。
三、线程通信
这里用个例题简单体现线程通信在wait()和notify()上的应用。
1.题目
小明-男,小红-女,用线程来分别对这两个人的姓名和性别进行输入输出。
2.思路
1.先考虑建几个类
这里建4个:含有main方法的类、Resource类(存放姓名、年龄这两个成员变量)、以及输入输出两个线程
2.涉及到的思维
①用构造方法传参来防止输出为null值的情况
②同步代码块的运用来解决线程安全隐患
③同步代码块中的任意对象应为本类对象
④两个线程也应该用的是同一把锁
注:同步技术重点是同一把锁
⑤用标记flag来判断输入输出中线程的等待及唤醒状态
⑥Iuput类是输入赋值,判断flag值是否为true,是则表明赋值完成,则进行等待,若不是,则将flag值改为true,并唤醒另一个线程;Output类是取值,判断flag值是否为false,是则表明取值完成,则进行等待,若不是,则将flag值改为false,进行取值并唤醒另一个线程。
3.代码
1.main函数代码:
public class Test {
public static void main(String[] args) {
Resource r=new Resource();
Input in=new Input(r);
Output ou=new Output(r);
Thread t0=new Thread(in);
Thread t1=new Thread(ou);
t0.start();
t1.start();
}
}
2.Resource类
public class Resource {
public String name;
public String sex;
//做标记
public boolean flag=true;
}
3.Iuput类
package Day24;
public class Input implements Runnable {
private int i=0;
Resource r;
public Input(Resource r){
this.r=r;
}
@Override
public void run(){
while (true){
synchronized (r){
if (r.flag){
try{r.wait();}catch(Exception ex){}
}
//这里对两个变量进行赋值
if (i%2==0){
r.name="小明";
r.sex="男";
}else {
r.name="小红";
r.sex="女";
}
r.flag=true;
r.notify();
i++;
}
}
}
}
4.Output类
public class Output implements Runnable {
Resource r;
public Output(Resource r) {
this.r=r;
}
@Override
public void run(){
while (true){
synchronized (r){
//判断标记,是false的话就等待
if (!r.flag){
try{r.wait();}catch (Exception ex){}
}
System.out.println(r.name+" "+r.sex);
//唤醒对方线程
r.flag=false;
r.notify();
}
}
}
}