进程和线程
并发和并行:
并发:
指两个或多个事件在同一个时间段内发生,交替执行
并行:
指两个或多个事件在同一时刻发生,同时执行
进程
进程是程序执行的一个实例,有独立的内存空间和系统资源
多个进程可以在CPU上并发执行
线程
如果把进程比作工厂车间的一条生产线,那么线程就是生产线上负责各个工序的工人。
一个进程可以拥有多个线程,但一个进程只能拥有一个主线程。当其他线程需要中途终止时,主线程负责杀死该线程。
线程时CPU调度和分派的基本单位,是进程中执行的最小单位,真正在CPU上运行的是线程。
多个线程在CPU上可以并发执行。
线程的创建
继承java.lang.Thread类
- 创建MyThread类继承Thread类,重写run()方法
public class MyThread extends Thread{
//run方法中就是线程要完成的任务
public void run() {
//输出0-100之间的偶数
for(int i = 0; i <= 100; i++) {
if(i % 2 == 0) {
System.out.println(i);
}
}
}
}
-
创建Test类
public class Test { public static void main(String[] args) { //创建线程对象 // Thread t = new MyThread(); //启动线程使用start方法,而不是run方法 // t.start(); } }
或者使用匿名内部类
public static void main(String[] args) { Thread myThread = new Thread() { @Override public void run() { for(int i = 0; i <= 100; i++) { if(i % 2 == 0) { System.out.println(i); } } } }; myThread.start(); }
实现java.lang.Runnable接口
- 创建MyRunnable类实现java.lang.Runnable接口,重写run方法
public class MyRunnable implements Runnable{
private int count = 0;
public void run() {
count++;
System.out.println("count="+count);
}
}
- 创建Test类
public static void main(String[] args) {
Runnable r = new MyRunnable();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
t1.start();
t2.start();
t3.start();
}
或者使用匿名内部类
public static void main(String[] args) {
//使用匿名内部类来创建线程
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("使用匿名内部类来创建线程");
}
};
Thread t = new Thread(r1);
t.start();
}
线程的使用步骤
- 定义线程
- 创建线程对象
- 启动对象
- 终止对象
线程的生命周期
- 新生状态
- 可运行状态
- 运行状态
- 阻塞状态
- 死亡状态
当线程对象调用start()方法时,进入可运行状态
当线程获得CPU后,进入运行状态
当线程由于某种原因,停止执行,陷入阻塞状态
原因:
- 执行wait()方法,陷入等待状态
- 执行sleep()方法,陷入睡眠状态
- 执行join()方法,陷入中断状态
- 同步阻塞,获取synchronized同步锁失败,因为锁被其他线程占用
注意:
线程从运行状态进入阻塞状态,阻塞状态解除进入可执行状态
sleep(long m)、wait(long m)、wait()
sleep(long m):
是Thread类的静态方法,可以在任意位置使用,若在同步方法或同步代码块中使用不释放锁,一定时间后可自然醒来
wait(long m):
一段时间后可自然醒来
wait():
是Object类的实例方法,只能在同步方法或同步代码块中使用,如果不被notify或notifyAll唤醒,将会被一直挂起,且会释放锁。
wait和notify必须要由同一个锁对象调用
多线程的安全问题
线程可以比作生产线上的工人,那么所有工人共享整个车间资源,但有些房间同时只能允许一个人进入,比如卫生间。当卫生间有人的时候,其他人就不能进去,倘若某人正在使用卫生间,突然一个人冲了进来,不管怎样,当时的空气总会尴尬了许多?
将以上情况带入到线程中会发生什么呢?
假设类中定义了一个实例变量,初始值为3,线程1做得事是让该变量的值加1,线程2做的事是让该变量的值加2,倘若线程1正在执行,线程2突然访问该变量,它在3还是4的基础上加2呢?
以小见大,如果有很多线程要访问该变量呢?
因此当多个线程访问同一资源——成员变量(实例变量或静态公共变量),就会产生多线程的安全问题
怎么解决多线程的安全问题呢?
日活生活在,我们会给卫生间上锁,同样的,在程序中我们可以上锁,防止多个线程同时读写同一块内存区域
解决方法(同步机制)
-
同步方法(synchronized)
非静态方法:锁对象默认为this
静态方法:本类的class属性–> class文件对象(反射)
-
同步代码块(synchronized)
代码块中锁对象可以是任意对象
多个线程必须使用同一个锁对象
锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行
使用了同一个锁对象,这个锁对象叫同步锁,也叫对象锁,或对象监视器
同步中的线程,没有执行完毕不会释放锁,同步代码块外的线程没有锁进不去同步代码块
3. Lock锁
在成员位置创建一个ReentrantLock对象
产生安全问题代码前用lock()方法
产生安全问题代码后用unlock()方法