目录
一、什么是多线程?
1、程序:
2、进程:
3、线程:
4、多线程:
5、多线程的三大特性:
二、多线程的实现方式
三、线程的生命周期
四、多线程同步:
什么是多线程安全:
如何解决线程安全的问题:
多线程同步的几种方法:
一、什么是多线程?
1、程序:
是含有命令和数据的文件,被存在磁盘或其他数据存储设备中,也就是说程序是静态的代码;
2、进程:
是程序的一次执行过程,是程序运行的基本单位,因此进程是动态的。系统运行一个程序既是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,他在计算机中一个指令接着一个指令的执行着,同时,每一个进程还占有某些系统资源如 CPU 时间,内存空间,文件,输入输出设备的使用权限等等。
3、线程:
与进程类似,但线程是一个比进程还小的执行单位,一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程只能共享同一块内存空间和一组系统资源,所以系统在产生一个线程或是在多个线程中切换工作时,负担要比进程小很多,正因如此,线程也被称之为 “ 轻量级进程 ” 。
换句话说,当程序在执行时,将会被操作系统载入内存中,线程是进程划分成更小的运行单位。线程和进程最大的不同在于基本上各个进程是独立的,而线程则不一定,因为同一个进程中的线程有一定几率会互相影响。从另一个角度来说,进程是属于操作系统的的范畴,主要是同一时间段内,可以同时执行一个以上的程序,而线程则是在同一程序内几乎同时执行一个以上的程序段。
4、多线程:
多线程就是在同一进程里面,同时有多个执行不同任务的线程。单线程一次只能执行一个任务,执行完当前任务才能执行下一个任务。多线程可以让多个任务同时执行。
5、多线程的三大特性:
原子性:保证数据一致性线程安全。
可见性:一个线程对另一个线程是否可见。
有序性:线程之间执行是否有顺序。
二、多线程的实现方式
多线程的实现方式常见的有:
1、继承Thread类:
public class MyThread extends Thread {
public void run(){
System.out.println("方法1.继承Thread类重写run方法");
}
public static void main(String args[]){
MyThread m1=new MyThread();
MyThread m2=new MyThread();
m1.start();
m2.start();
}
}
2、实现Runnable接口:
public class MyThread_2 implements Runnable {
public void run(){
System.out.println("方法2:通过实现Runnable接口,重写run方法");
}
}
public class MyThread{
public static void main(String args[]){
MyThread_2 my2=new MyThread_2();
Thread thread=new Thread(my2);
thread.start();
}
}
3、实现Callable接口通过Future Task包装器来创建Thread线程:
三、线程的生命周期
就绪状态:当调用线程对象的start()方法,线程进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好准备,随时等待CPU的调度执行,并不是说执行了t.start()此线程就能立刻执行;
运行状态:当CPU开始调度处于就绪状态的线程时,此时线程才得以真正的执行,即进入到运行状态。就绪状态时进入到运行状态的唯一入口,也就是说,线程想要进入运行状态执行,首先就必须处于就绪状态中;
堵塞状态:处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入堵塞状态,知道进入就绪状态,才有机会再次被CPU调用以进入运行状态,根据堵塞产生的原因不同,堵塞状态可以分为一下三种:
- 等待堵塞:运行状态中的线程执行 wait() 方法,使本线程进入到等待堵塞状态;
- 同步堵塞:线程在获取 synchronized 同步锁失败(因为锁被其他线程占用),它会进入同步堵塞状态;
- 其他堵塞:通过调用线程的 sleep() 或 join() 或发出来 I/O 请求时,线程会进入堵塞状态。当 sleep() 状态超时、join() 等待线程终止或者超时、或者 I/O 处理完毕时,线程重新装入就绪状态。
死亡状态:线程执行完了或者因异常退出了 run() 方法,改线程结束生命周期。
四、多线程同步:
什么是多线程安全:
线程安全就是说多个线程访问同一代码,不会产生不确定的结果。在多线程环境中,当各个线程不共享数据的时候,即都是私有成员( private ),那么一定是线程安全的,但这种情况并不多见,在 多数情况下都需要共享数据,在时候就需要进行适当的同步控制了。线程安全一般都涉及到synchronized ,就是一段代码同时只能由一个线程来操作,不然中间可能会出现不可预计的结果。
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这些代码。如果每次运行得到的结果和你单线程运行的结果是一样的,而且其他的变量的值也和预期的值也和预期的值是一样的,就是线程安全的。
如何解决线程安全的问题:
使用多线程之间同步 synchronized :将可能会发生数据冲突问题(线程不安全问题)的代码放入同步代码块中,只能让当前一个线程进行执行,被包括的代码执行 完成之后才会释放,然后才能让后面的代码执行;
使用锁( lock ):每个锁只能由一个线程持有,代码执行完成之后释放锁,然后才能让其他线程进行执行,这样的话就可以解决问题;
多线程同步的几种方法:
1、使用同步代码块
synchronized (同一数据) {
可能会发生线程问题的代码
}
private Object mutex = new Object();//自定义多线程同步锁
public void sale() {
synchronized (mutex) {
if (trainCount > 0) {
try {
Thread.sleep(10);
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName() + "第" + (100 - trainCount + 1) + "次");
trainCount--;
}
}
}
2、使用同步函数
在方法上修饰 synchronized 称为同步函数
public synchronized void sale() {
if (trainCount > 0) {
try {
Thread.sleep(40);
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName() + "第" + (100 - trainCount + 1) + "次");
trainCount--;
}
}
3、使用静态同步函数
public static void sale() { //前面有static修饰了 静态的不需要被实例化
synchronized (MyThread.class) { //当前类的clas
if (trainCount > 0) {
System.out.println(Thread.currentThread().getName() + "第" + (100 - trainCount + 1) + "次");
trainCount--;
}
}
}
本章文档的内容就到这里,谢谢大家的观看!!!



