一、认识线程
1.进程是程序的一次动态执行过程。
2.进程的特点
- 进程是系统运行程序的基本单位
- 每一个进程都有自己的独立的一块内存空间,一组系统资源
- 每一进程的内部数据和状态都是完成独立的
3.线程:线程是进程中执行运算的最小单元,一个进程在执行过程中可以产生多个线程,而线程必须在某个进程内执行。
4.线程是进程内部的一个执行单元,是可完成一个独立任务的顺序控制流程。
5.多线程:在一个进程中同时运行多个线程,用来完成不同的工作,就是多线程。
多个线程交替占用cpu资源,而非是真正的并行执行!!!
6.线程和进程的区别:
- 一个进程中至少有一个线程
- 资源分配给进程,同一进程的所有线程共享该进程的所有资源
- 处理机制分配给线程,就是真正的在处理机上运行的是线程。
7.多线程的好处:
- 可以带来良好的用户体验,避免程序执行中速度慢而导致计算机死机或者白屏。
- 可以提高计算机系统的的利用效率。常见的如迅雷下载等。
二、创建线程
1.什么是主线程?
没个程序至少拥有一个线程,叫做主线程。如:main()方法是主线程的入口。
2.创建线程的方式?
通常有两种方式: 继承Thread类和实现Runnable接口。一般是实现接口的方式!!!
使用Thread类创建线程:
public class MyThread extends Thread{ //继承Thread的方式创建线程
private int count=0; //计数
//重写run方法,执行任务操作的方法
public void run() {
while(count<100) {
count++;
System.out.println(Thread.currentThread().getName()+"的值是:"+count);
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
MyThread mt=new MyThread(); //实例化对象
MyThread mt2=new MyThread(); //实例化对象
mt.start(); //启动线程
mt2.start();
}
}
//注意:run()方法是线程执行要操作任务的方法,线程要执行的操作代码都需要写在run()方法中
注意:在启动线程的时候,一定是调用start()方法,才能达到多条线程交替执行的效果。如果是调用run()方法,那么就只有主线程一条执行路径,分别一次去调用run()方法,就达不到多线程交替执行的效果。
实现Runnable接口的方式:
public class MyRunnable implements Runnable { //实现Runnable接口方式创建线程
private int count; //计数
/**
* 重写方法
*/
public void run() {
while(count<100) {
count++;
System.out.println(Thread.currentThread().getName()+"的值是:"+count);
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
//1.创建线程对象
Runnable mr=new MyRunnable(); //多态,父类类型指向之类对象
Thread t=new Thread(mr,"线程A"); //创建线程的对象,可以修改线程名称
t.start(); //调用方法,启动线程
Thread t1=new Thread(mr,"线程B"); //创建线程的对象,可以修改线程名称
t1.start();
}
}
两种创建线程的方式都有不同的应用:
直接继承Thread类的方式使用简单,使用于单重继承的情况。
实现Runnable接口的方式,可以方便资源共享,避免java中的单继承,可以达到多继承的效果!!
三、线程的状态
1.线程的5种状态:新生(新建),就绪,运行,阻塞,死亡状态。
新生状态:
- 在创建线程对象之后,没有调用start()方法之前
就绪状态:
- 调用start()方法之后,但是还没有抢到cpu资源。
运行状态:
- 抢到cpu资源,已经真正运行run()方法
阻塞状态:
- 一个正在运行的线程因为某些原因不能运行,进入了阻塞状态。阻塞状态是一种不可运行的状态。
导致一个线程被阻塞的原因:
调用了Thread类的sleep()方法。
等待用户输入。
对象锁被占用。
死亡状态:
- 一个线程run()方法已经运行完毕,stop()方法被调用,或者在与运行过程中出现未捕获的异常。
四、线程调度
1.什么叫线程调度?
就是按照特定机制为多个线程分配cpu的使用权。
2.线程的优先级
线程的优先级用1~10表示,1为最低,10最高,默认是5.
如:Thread.Min_PRIORITY
3.实现线程调度的方法
join()方法:使当前线程暂停执行,等待调用该方法的线程结束后再执行本线程。
但是,在执行的过程中,有可能join()没有起到作用,子线程执行完毕了,那么这也是正常情况!
join()方法的作用是阻塞线程!!!
注意:join()方法是实例方法,需要通过线程对象去调用!
sleep()方法:使当前线程进入休眠(阻塞)状态,停止执行,时间是毫秒,休眠时间过后会再次进入运行状态,前提是能够抢到cpu资源!
该方法是静态方法,可以通过Thread类直接调用!
yield()方法:让当前线程暂停,让其他线程执行,但当前线程还是可运行状态,并不转为阻塞状态!
此方法是线程礼让,只是提供了一种可能,并不一定能保证礼让!!!
五、线程同步
1.为什么需要线程同步?
因为在多个线程共享同一资源时,一个线程未完成全部操作的时候,其他线程进去操作的时候,会操作线程的不安全问题,这就需要线程同步!
2.什么叫做线程同步?
当多个线程需要访问同一资源时,需要以某种顺序来确保该资源在某一时刻只能被一个线程使用的方式,就是线程同步!
3.同步的方式:
同步方法:在方法名前加上synchronized关键字来声明同步方法。
如:public synchronized void show(){ }
作用:为当前线程声明锁,只允许一个线程操作数据,完了之后其他线程才能够进来操作数据。
同步代码块:synchronized (this){ } 同步当前的对象用this变表示。
作用:和同步方法一样,保证数据的安全性,但是它可以针对任意代码块,灵活性比较高!
注意:当一个线程代码块上 synchronized(this){ }上锁后,其他的synchronized (this) { }同样并锁定,也一样不能被访问,但是其他线程可以访问该资源的非 synchronized(this)的代码。
4.死锁
产生死锁的原因?
多个线程都处于等待状态而无法唤醒时,就构成了死锁!
避免死锁的方法?
线程应某个条件未满足而受阻,不能让其继续占有资源,如果有多个对象需要互相访问时,应该确定线程获得锁的顺序,并保证整个程序以相反的顺序去释放锁!