本人小白一枚,欢迎大家一起讨论学习,如有错误,还望大家指教。
简述:
在我们阐述线程时,我们先了解下进程,进程是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到小王的过程。而线程是进程的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程,而一个进程中可以存在多个线程,简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。我们把操作系统中的多个任务称为进程,而把程序中的多个任务称为线程。
并发:指两个或多个事件在同一个时间段内发生。
并行:指两个或多个事件在同一时刻发生(同时发生)。
线程调度:
- 分时调度: 所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。
- 抢占式调度: 优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),java使用的为抢占式调度,在电脑里我们可以自己手动设置线程的优先级。抢占式具有随机性,因为多个线程都在获取CPU的执行权,谁获取了CPU的执行权,谁就运行,明确一点,在某一个时刻,只能有一个程序在运行(多核除外),CPU在做着快速切换,切换的速度相对我们而言很快,从而我们看上去像是在同时运行。
线程使用:
创建线程有两种方法:一种是继承java.lang.Thread
,另一种是实现java.lang.Runnable类
。
方法一:继承Thread类
- 创建子类,继承
java.lang.Thread类
- 重写Threa父类中的run方法。
- 创建子类对象并调用start()方法启动线程。
执行流程:当程序启动运行时,java的JVM会启动一个进程,main方法运行时会创建一个主线程,当我们创建子类线程对象并调用start方法时,另一个线程也启动了,当执行线程的任务结束了,线程会自动在栈内存释放,只有当所有线程都结束时,进程才会结束。我们可以看下图来了解下内存中的变化:start方法有两个作用:1、开始线程 2、调用run方法
Thread方法:
- 构造方法:
public Thread():
创建一个新的线程对象。public Thread(String name):
创建线程对象并指定线程名称。public Thread(Runnable target):
这是创建的第二种方法,我们在下面进行介绍。public Thread(Runnable target, String name):
同上,并指定线程名称。
- 常用方法:
public String getName():
获取当前线程的名称。public void srart():
开启线程并调用此线程的run方法。public void run():
此线程要执行的任务代码。public static void sleep():
使当前正在执行的线程以指定毫秒数暂停(暂时停止执行)。public static Thread currentThread():
返回对当前正在执行线程对象的引用。
方法二:实现Runnable接口
- 定义Runnable接口的实现类,并重写该接口的run方法。
- 创建实现类对象,并将此对象作为Thread的参数创建Thread对象。
- 调用Thread对象的start方法类启动线程。
public class RunnableDmeo1 implements Runnable {
@Override
public void run() {
for (int i = 1;i <= 5;i++) {
System.out.println(Thread.currentThread().getName() + "---->" + i);
}
}
}
public class MainDemo {
public static void main(String[] args) {
// 创建Runnable实现类对象
Runnable runnable = new RunnableDmeo1();
Thread thread = new Thread(runnable);
thread.start();
for (int i = 1;i <= 5;i++) {
System.out.println(Thread.currentThread().getName() + "---->" + i);
}
}
}
main---->1
Thread-0---->1
Thread-0---->2
main---->2
main---->3
main---->4
main---->5
Thread-0---->3
Thread-0---->4
Thread-0---->5
Thread和Runnable的区别:
继承Thread类:线程代码放在Thread子类中的run方法中。
实现Runnable接口:线程代码放在实现类中的润方法中。
实现Runnable接口比继承Thread类的好处:
- 避免了单继承的局限性。
- 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
- 适合多个相同的程序代码的线程去共享一个资源。
- 线程池只能放入实现Runnable或Callable类线程,不能直接放入继承Thread的类。
扩展:在java中,每次程序运行至少启动两个线程,一个是main线程,一个是垃圾收集线程,因为每当使用java命令去执行一个类的时候,实际上都会启动一个JVM,每一个JVM其实就是在操作系统中启动了一个进程。
线程安全:
什么是线程安全问题:就是当多条语句都在操作同一个线程共享的数据时,一个线程只执行了一部分还没有执行完,另一个线程就参与起来,导致了共享数据的错误 。解决的办法就是 对于多条操作共享数据的语句,只能让一个线程执行完,在执行的过程中,其他的线程是不可以参与进来的,java对于多线程安全问题,提供了一下三种的解决方式:
- 同步代码块
- 同步方法
- 锁机制
一、同步代码块:
使用synchronized
关键字对方法中操作共享数据的区块进行标识,表示对这个区块的共享数据实行互斥访问。就是最多允许一个线程拥有同步锁,谁拿到同步锁就可以进步代码块,其他线程在外面等着该锁执行完。
格式:
synchronized (同步锁) {
需要同步操作的代码
}
同步的前提:锁对象可以是任意类型。
- 必须要有两个或两个以上的线程。
- 必须是多个线程使用同一个锁。
案例:解决售票问题
public class Ticket implements Runnable {
private int ticket = 100;
Object lock = new Object();
@Override
public void run() {
while (true) {
synchronized (lock) {
if (ticket > 0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖:" + ticket--);
}
}
}
}
}
二、同步方法
使用synchronized
修饰的方法。叫做同步方法,保证A线程执行该方法的时候,其他线程同样在方法外面等待。
格式:
public synchronized void method() {
对于会产生线程安全问题的代码
}
案例:解决售票问题
public class Ticket implements Runnable {
private int ticket = 100;
@Override
public void run() {
while (true) {
sellTicket();
}
}
/**
* 使用synchronized修饰,锁对象是this
*/
private synchronized void sellTicket() {
if (ticket > 0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖:" + ticket--);
}
}
}
同步函数是使用哪一个锁呢?
函数需要被对象调用,那么函数都有一个对象的引用,就是this,所以同步函数使用的锁就是this。
如果同步函数被静态修饰,使用的锁是什么呢?
静态进内存中,没有本类对象,但是一定有该类对象的字节码类名.class,所以静态同步方法使用的锁就是该方法所在类的字节码文件对象,即类名.class。
三、Lock锁
java.util.concurrent.locks.Lock
机制提供了比synchronized代码块
和synchronized方法
更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外,Lock更体现了面向对象。
Lock锁使用如下:
public void lock():
加同步锁。public void unlock():
释放同步锁。
public class Ticket implements Runnable {
private int ticket = 100;
//创建锁对象
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
sellTicket();
}
}
private void sellTicket() {
// 加同步锁
lock.lock();
if (ticket > 0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖:" + ticket--);
}
// 释放锁
lock.unlock();
}
}
转载:https://blog.csdn.net/qq_43138206/article/details/102054233