多线程
多线程示例:
一家公司账户有100元,这天两个会计同时操作了公司的银行账户,会计A取50,会计B存100,可能会发生以下几种情况:
- 账户余额变成50(A和B同时读到了账户余额等于100,A最后更改了账户)
- 账户余额变成200(A和B同时读到了账户余额等于100,B最后更改了账户)
- 账户余额变成150 (A和B顺序读取到账户余额)
1、进程
- 是执行中的应用程序
- 一个进程可以包含一个或多个线程
- 一个进程至少要包含一个线程(比如Java中运行main方法的主线程)
比如:B站看视频的同时,还可以点赞收藏等等,这些操作都是Bilibili这个应用程序的子任务,也就是这个进程的子任务
2、线程
- 线程本身依靠进程(程序)运行
- 线程是程序中的顺序控制流,只能使用分配给程序(进程)的资源和环境
解释:运行一个Java程序的实质是启动一个Java虚拟机进程,也就是说一个运行的Java程序就是一个Java虚拟机进程。
线程是进程中可独立执行的最小执行单元,一个进程中有多个进程,同一线程中的线程共享该进程的资源(内存、空间、变量、方法)
2.1、单线程
程序中只存在一个主线程,实际上主方法(main)就是一个主线程
线程是进程中所要完成的一个计算过程,也被称为进程的任务
2.2、多线程
多线程是在一个进程中同时运行多个任务
多线程的目的:更好的使用cpu资源,提高程序的计算效率
2.3、串行、并行、并发
2.3.1 串行:一个人做一件件做所有任务
2.3.2 并发:一个人,交替的做多个任务
即一个人先执行一个任务,在这个任务执行期间,有一段时间人是空闲的,他利用这段时间再去执行另一个任务,这样交替执行任务。
示例:蒸馒头的同时,馒头没熟之前,这段时间可以拖地、洗衣服等等
2.3.3 并行:多个人,同时做多个任务;总耗时取决于耗时最长的任务
3、创建线程的方式
- 继承Thread类,extends Thread
- 实现Runable接口,implement Runable
- 通过Collection接口和FutureTask对象创建线程
- 通过线程创建对象
创建线程
//方法1
package d17thread;
public class Demo {
public static void main(String[] args) {
CrThr c = new CrThr();
c.start();
}
}
class CrThr extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("创建线程");
}
}
//方法2
package d17thread;
public class Demo {
public static void main(String[] args) {
CrThr c = new CrThr();
Thread t = new Thread(c);
t.start();
}
}
class CrThr implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("创建线程");
}
}
package d17thread;
/*创建线程类的方式
* 1.继承Thread类,extends Thread
* 2.实现runable接口,implements Runable
* 3.通过Callable接和FutureTask对象创建线程
* 4.通过创建线程池创建对象
*/
public class Test {
public static void main(String[] args) {
//创建线程对象
Worker w = new Worker();
w.setName("tt1");
//启动线程类
w.start();
/*
* 调用start()方法不是立即调用run()
* 而是先去争用cpu时间片,一旦获取cpu时间片,才会去调用run()方法
*/
Worker2 w2 = new Worker2();
Thread t = new Thread(w2,"t1");
t.start();
}
}
class Worker extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
/*
* sleep()属于Thread类,wait()类属于Object类
* sleep()方法表示线程休眠指定的时间,休眠时间结束,线程继续执行
* wait()方法表示线程等待,此时线程会让出cpu资源,知道其他线程通过nodify()/nodifyAll()方法唤醒该线程
*
*/
try {
sleep(1000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("线程开始执行");
}
}
class Worker2 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
System.out.println("重写Runable接口");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p8f51hCX-1666355724888)(C:\Users\19912\AppData\Local\Temp\1665997650707.png)]
3.1、线程的一生
新建、就绪、运行、阻塞、死亡
- 新建:创建线程对象后,即进入阻塞状态
- 就绪:当调用线程对象的start()方法,线程进入就绪状态,处于就绪状态的线程,只是说明该线程已经做好了准备,随时等待cpu调度执行,并不是说执行了start()方法线程就会立即执行
- 运行:当cpu开始调度处于就绪状态的线程时,此时线程才开始执行(执行run()方法),即进入运行状态,注意,就绪状态是进入到运行状态的唯一入口,也就是说,线程想要进入运行状态执行,首先必须处于就绪状态种
- 阻塞:处于运行状态中的线程由于某种原因,暂时放弃对cpu的使用权,停止执行,此时进入阻塞状态,直到其重新进入到就绪状态
根据阻塞产生的原因不同,阻塞状态又分为三种
- 等待阻塞:运行状态中的线程执行Object.wait()方法
- 同步阻塞:线程在获取synchronized内部锁失败(因为所被其他线程占用)
- 其他阻塞:通过调用线程的Thread.sleeo()、join()方法或发出I/O请求时,线程会进入到阻塞状态,当sleep()状态超时(sleep休眠结束)、join()方法等待线程终止或超时,或者I/O处理完毕时,线程重新进入就绪状态
- 死亡:线程执行完毕或者因为异常推出了run()方法,该线程结束生命周期,线程销毁
4、线程的同步机制
同步机制:多个线程按照一定顺序执行的方案
4.1 同步锁 synchronized
- Java 中的任何一个对象,都有唯一一个与之相关联的锁
- 这种锁被称为监听器(monitor)或内部锁(Intrinsic Lock)
- 内部锁是一种排它锁,即哪个线程拿到锁,该线程才能电泳同步方法,其他线程阻塞状态
- 内部锁是通过synchronized关键字实现的
- synchronized关键字可以用来修饰方法以及代码块
package d17thread;
public class Th1 {
/*
* 同步机制:多个线程按照一定顺序执行的方案
*
* 同步锁 synchronized
*
* Java 中的任何一个对象,都有唯一一个与之相关联的锁
* 这种锁被称为监听器(monitor)或内部锁(Intrinsic Lock)
* 内部锁是一种排它锁,即哪个线程拿到锁,该线程才能电泳同步方法,其他线程阻塞状态
* 内部锁是通过synchronized关键字实现的
* synchronized关键字可以用来修饰方法以及代码块
*/
public static void main(String[] args) {
Thre t = new Thre();
t.start();
Thre t1 = new Thre();
t1.start();
}
static int count;
static synchronized void add() {
for (int i = 0; i < 80000; i++) {
count++;
System.out.println(count);
}
}
}
class Thre extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
Th1.add();
}
}
package d17thread;
public class Th1 {
/*
* 同步机制:多个线程按照一定顺序执行的方案
*
* 同步锁 synchronized
*
* Java 中的任何一个对象,都有唯一一个与之相关联的锁
* 这种锁被称为监听器(monitor)或内部锁(Intrinsic Lock)
* 内部锁是一种排它锁,即哪个线程拿到锁,该线程才能电泳同步方法,其他线程阻塞状态
* 内部锁是通过synchronized关键字实现的
* synchronized关键字可以用来修饰方法以及代码块
*/
public static void main(String[] args) {
Thre t = new Thre();
t.start();
Thre t1 = new Thre();
t1.start();
}
static int count;
static synchronized void add() {
for (int i = 0; i < 80000; i++) {
count++;
System.out.println(count);
}
}
}
class Thre extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
Th1.add();
}
}