Java多线程概述及线程同步
多线程概念
一个进程可能由多个线程组成。多个线程可以同时运行并处理相同或不同的程序。
进程: 程序的一次执行过程,或是正在运行的一个程序,程序加载到内存当中了就变成了一个进程, 程序是一段代码
线程: 进程可进一步细化为线程,线程是一个程序内部的一条执行路径,比如360可以同时执行体检,杀毒,清理功能 360就是支持多线程的
在JVM中 堆和方法区是进程之内共享的,所以他们是在线程之间共享的,所以会出现一些安全隐患,java.exe这个进程至少有三个线程 main()主线程,gc()垃圾回收线程,异常处理线程
并行与并发
并行: 多个CPU同时执行多个任务,比如:多个人同时做不同的事
并发: 一个CPU(采用时间片)同时执行多个任务
举例:
4个篮球场 4伙人 每一伙人自己玩自己的 就是并行
1个篮球场 4伙人一起玩 同一时刻只有一伙人可以上去玩 这个叫做并发
多线程的实现方式
多线程的实现方式(获取一个可执行线程的方式) 四种:
1、继承Thread类,重写run方法(常用)
2、实现runnable接口,实现run方法(常用)
3、实现Callable接口,实现timertask方法
4、线程池获取
继承Thread类重写run方法 创建相应对象调用start方法执行线程
public class MyThread extends Thread {
//1.重写Thread类中的run方法
//run方法中书写的就是线程运行时要执行的代码
// 将run方法当做main方法书写要执行的代码即可
@Override
public void run() {
// 线程运行时执行的方法
int i = 1;while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(i);
i++;
}
}
}
实现runnable接口 实现run方法 创建runnable对象交由thread对象执行
public class MyRunnable implements Runnable{
//2.实现Runnable中的run方法
//run方法中书写的就是线程运行时要执行的代码
// 将run方法当做main方法书写要执行的代码即可@Override
public void run() {
// 线程运行时执行的方法
int i = 1;
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
i++;
}
}
}
本质上是创建任务对象,然后将任务对象交由线程对象去完成
区别: 重写thread时每次单独创建指定任务的线程,实现runnable接口是创建空白线程执行指定任务。
常用两种实现方式的异同点:
(1)底层代码都是一样的,只不过Thread类已经实现了runnable接口,在继承重写时,只不过将默认调用runnble接口的方法改为自己书写的执行方法,runnable本身就是交由Thread对象进行执行的,一个Thread对象如果没有传入Runnable对象那么不会执行,本质上来说实现runnable接口才是实现获取一个可执行线程的最佳方式(一般情况下)
(2)继承Thread类使用的是继承重写方式,实现runnable接口使用的实现,java中继承是单继承的,但是runnable接口可以多继承多实现,并且在实现runnable接口的同时还可以继承其他类
(3)继承Thread类每个线程对象使用的书写为当前线程指定属性,但是runnable接口创建的任务对象可以被多个线程共用,实现同一资源的共享(当然也可以通过static实现)
Thread多线程常用方法:
线程优先级:
Thread与Runnable使用场景
1、当不需要进行多线程共享资源或每个线程处理事物不同时使用Thread
2、当进行资源共享处理相同事物时使用Runnable
3、当需要进行线程同步时使用Runnable
线程的生命周期
新建态(初始态)
使用new创建线程对象时产生的状态
就绪态
调用start方法将线程由新建态转换为就绪态
运行态
系统分配资源给相应准备就绪的线程,自动调用方法后进入运行态
系统会一直监控就绪态的线程,如果有资源会立即分配并执行
阻塞态
调用阻塞或执行阻塞方法,使当前线程进入阻塞态
死亡态
线程运行结束后进入死亡态
线程同步机制
使多个线程在运行时执行相同代码时有且只允许一个线程执行。
线程同步,指的是完成同一功能的代码或拥有相同标识的代码在同一时间只允许一个线程执行
线程同步机制就是使用同步关键字 synchronized
同步代码块方式
synchronized(同步对象(锁)){
要同步的代码
}
同步对象可以理解为锁,多个不同线程同步代码块如果想要唯一执行,那么需要加入同步代码块中,并用相同的锁对象进行同步的校验
public class RunnableTest implements Runnable {
int ticket = 5;
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}synchronized (this) {
if (ticket > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "售票" + ticket--);
}
}
}
}
public static void main(String[] args) {
RunnableTest rt = new RunnableTest();
Thread t1 = new Thread(rt);
Thread t2 = new Thread(rt);
t1.start();
t2.start();}}
同步方法
synchronized修饰方法使方法变为同步方法,在多个线程执行同一个方法时进行校验
public class RunnableTest implements Runnable {
int ticket = 5;
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
a();
}}
public synchronized void a() {
if(ticket>0) {
try {Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("售票"+ticket--);
}
}
public static void main(String[] args) {
RunnableTest rt = new RunnableTest();
Thread t1 = new Thread(rt);
Thread t2 = new Thread(rt);
t1.start();
t2.start();
}
}
线程通信
概念:
线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体,线程之间的通信就成为整体的必用方式之一。
当线程存在通信指挥,系统间的交互性会更强大,在提高CPU利用率的同时还会对线程任务在处理过程中进行有效的把控与监督。
为了支持多线程之间的协作,JDK提供了两个非常重要的接口线程等待wait()方法和通知notify()方法。这两个方法并不是在Thread类中的,而是输出Object类。这也意味着任何对象都可以调用这2个方法:
1、wait() 和 notify()必须配合synchrozied关键字使用,无论是wait()还是notify()都需要首先获取目标对象的一个监听器。
2、wait()释放锁,而notify()不释放锁。
等待唤醒都是通过锁对象进行的,会将当前执行同步代码块获得锁对象的线程进入等待状态,等待唤醒
start方法与run方法的区别(为什么重写的时run方法调用start方法开启线程)
1、状态转换的区别
start方法将线程由新建态转换为就绪态,run方法是就绪态转换为运行态,
2、调用方的区别
start由调用方调用执行。Run方法由系统自动调用,线程运行需要系统提供资源。
run方法可不可以直接调用?
可以,但是如果直接调用就相当于普通方法,没有系统分配资源开辟线程,而是在主线程中直接运行。 与多线程无关,相当于定义了一个run方法并创建对象调用。
一个线程要想运行需要满足两个条件。
1.获取运行资源(开辟线程)2.获取锁
yeid方法: 线程礼让方法,不会释放锁,但是会让出资源,如果资源充足的情况下,调不调用没有影响
wait方法: 线程等待方法,将资源与锁全部让去进入类似与休眠的状态,等待唤醒
将线程由运行态转换为阻塞态
sleep方法 线程休眠方法,资源与锁都不会释放,将线程由运行态转换为阻塞态,当休眠时间到达,自动由阻塞态转换为就绪态
notify/notifyAll方法: 线程唤醒方法,通常与wait方法一起使用,用于唤醒等待的线程,使其由阻塞态进入就绪态
死锁的概念
多个线程执行,拥有两个以上的锁,互相占用锁资源
死锁是用来避免的不是用来解决的,对于死锁的解决,就是锁的释放
sleep() 方法不会释放“锁标志”,也就是说如果有synchronized同步块,其他线程仍然不能访问共享数据
wait() 方法与 sleep() 方法的不同之处在于,wait() 方法会释放对象的“锁标志”。
yield() 方法和 sleep() 方法类似,也不会释放“锁标志”
上一篇: 二十年前的我们
下一篇: 还磨蹭个啥,把你老婆叠在箱子里赶快走吧