Java 多线程与线程安全基础

codeseeker
• 阅读 1140

进程与线程

进程: 指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以有多个线程.

线程: 指进程中的一个执行任务(控制单元),是程序执行时的最小单位,是CPU调度和分派的基本单位,一个进程可以运行多个线程,多个线程可共享资源.

并行与并发

并行: 多个进程同时运行,发生在多CPU中,一个进程占一个CPU,所有进程同步运行,互相之间不抢夺资源

并发: 多个进程间隔运行,发生在单CPU中,每个进程切换运行,切换时间很短,所以看起来好像是同时在运行,互相之间抢夺资源

同步与异步

同步: 多个线程有序执行,前面的执行完了后面的补上,在前面线程运行时其他线程都在等待

异步: 多个线程同时进入就绪状态,等待CPU的统一调度,当线程进入运行状态时其他线程可以访问其他资源,把访问资源的空闲时间利用起来,能提高效率,也就是多线程机制

线程分类

应用线程: 前台线程,执行各种具体任务的线程,一个程序启动至少有一个应用线程(main线程)和一个守护线程(GC)

守护线程: 后台线程,随着前台线程的全部死亡,守护线程也会自动死亡

线程的创建和启动

继承方式创建

自定义类继承Thread

重写run方法,把线程任务封装进run方法中

创建自定义对象

自定义对象调用start()方法,开启线程

// 继承方式创建线程

public class MyThreadDemo extends Thread { // 继承Thread类创建线程

// 重写run方法,把线程任务封装进run方法

@Override

public void run() {

for (int i = 0; i < 10; i++) {

System.out.println("吃了" + (i + 1) + "顿");

}

}

}

// 开启线程

public class MyThreadTestDemo {

public static void main(String[] args) {

// 创建线程对象,可以使用多态的方法创建, Thread m = new MyThreadDemo();

MyThreadDemo m = new MyThreadDemo();

// 开启线程

m.start();

// 线程开启后进入独立模块,CPU调度随机,也就是说两个线程随机运行

for (int i = 0; i < 10; i++) {

System.out.println("睡了" + (i + 1) + "天");

}

}

}

接口实现方式创建

自定义类实现Runnable接口

重写run方法,把线程任务封装进run方法

创建自定义对象

创建Thread类对象,将自定义对象作为参数传入Thread类创建对象的构造器

Thread对象调用start()方法,开启线程

// 实现Runnable接口创建线程

public class MyRunnable implements Runnable{ // 实现Runnable接口创建线程

// 重写run方法,将线程任务封装进run方法

@Override

public void run() {

for (int i = 0; i < 10; i++) {

System.out.println("吃了" + (i + 1) + "顿");

}

}

}

// 启动线程

public class MyRunnableDemo {

public static void main(String[] args) {

// 创建线程对象

MyRunnable m = new MyRunnable();

// 创建Thread类对象,将线程对象作为构造器参数传过去

Thread t = new Thread(m);

// 开启线程

t.start();

for (int i = 0; i < 10; i++) {

System.out.println("睡了" + (i + 1) + "天");

}

}

}

线程的生命周期

当程序创建一个线程以后,线程处于新建状态,无法被CPU调度,需要调用start()方法开启线程,调用start()方法之后线程进入就绪状态,等待CPU调度

当就绪状态的线程被CPU调度时进入运行状态,运行状态和就绪状态可以相互切换,当多个线程切换速度很快时,我们看起来就像多个线程在同步运行,这就是并发

运行状态可以切换到等待状态,它会等待另一个线程来执行一个任务,一个等待状态的线程只有通过另一个线程通知它转到可运行状态,才能继续执行

运行状态转到等待状态可以设置一个计时器,等待特定的时间之后唤醒线程对象

运行状态遇到异常或者其他特殊状况导致不能运行时进入阻塞状态,让出CPU调度,停止自身运行

当线程执行完或者抛出未捕获的异常和错误时,或者调用线程的stop()方法,线程终止,生命周期结束

操作线程的方法
智汇代理申请https://www.kaifx.cn/broker/t...

join方法: 主要作用是同步,它可以使得线程之间的并行执行变为串行执行.比如在A线程中调用了B线程的join()方法时,表示只有当B线程执行完毕时,A线程才能继续执行

sleep方法: 让正在执行的线程暂停一段时间,进入阻塞状态.

// sleep(long milllis) throws InterruptedException; 毫秒为单位

Thread.sleep(1000); // 需要处理异常,使用try-catch

1

2

线程的优先级: 每个线程都有优先级,优先级的高低只和线程获得执行机会的次数多少有关,并不是说优先级高的就一定先执行,哪个线程的先运行取决于CPU的调度.Thread对象的setPriority(int x)和getPriority()用来设置和获得优先级,x的值不能大于线程执行的次数,否则报IllegalThreadStateException异常

后台线程: 守护线程,一般用于为其他线程提供服务,若前台线程都死亡,后台线程自动死亡.Thread对象setDaemon(true)用来设置后台线程,setDaemon(true)必须在start()调用前,否则抛出IllegalThreadStateException异常

线程安全性: 当多线程并发访问同一资源时会导致线程出现安全性的原因

需求: 现有100个苹果,现在有3个人去吃.

继承方式

// 线程类,继承Thread类

public class SafeThread extends Thread {

private int number = 100; // 苹果数量

public SafeThread(String name) { // 构造器,调用父类构造器把值传给父类

super(name);

}

// 重写run方法,把线程任务封装进去

public void run() {

for (int i = 0; i < 100; i++) {

if (number > 0) {

System.out.println(Thread.currentThread().getName()

+ "吃了第" + number-- + "个苹果");

}

}

}

}

// 测试类

public class SafeThreadDemo {

public static void main(String[] args) {

// 创建对象

Thread s1 = new SafeThread("xx");

Thread s2 = new SafeThread("yy");

Thread s3 = new SafeThread("zz");

// 开启线程

s1.start();

s2.start();

s3.start();

}

}

// 结果是3个人每个人都吃了100个苹果,继承方式多个线程不能共享同一资源

实现接口方式

// 创建线程类实现Runnable接口

public class SafeRunnable implements Runnable {

private int number = 100; // 苹果数量

// 重写run方法,把线程任务封装进去

public void run() {

for (int i = 0; i < 100; i++) {

if (number > 0) {

System.out.println(Thread.currentThread().getName()

+ "吃了第" + number-- + "个苹果");

}

}

}

}

// 测试类

public class SafeRunnableDemo {

public static void main(String[] args) {

// 创建线程对象

SafeRunnable s = new SafeRunnable();

// 创建Thread对象把线程对象当作参数参数传过去,开启线程

new Thread(s,"xx").start();

new Thread(s,"yy").start();

new Thread(s,"zz").start();

}

}

// 结果是3个人平分了100个苹果,实现方式可以多个线程共享同一资源

继承方式与实现方式的区别

Java中类是单继承,如果继承了Thread类就不能再继承其他类了,而实现方式不但可以再继承其他类,还可以实现多个接口,所以实现方法比较好用

继承方式多个线程没法共享同一资源,实现方式可以做到多个线程共享同一资源

线程同步

当多线程访问同一资源对象的时候可能会出现线程不安全的问题,实现Runnable接口创建线程的时候看起来可能没有问题,但是出现网络延迟的时候就会出现,这里用线程睡眠来模拟网络延迟

// 添加线程睡眠

public class SafeRunnable implements Runnable {

private int number = 100; // 苹果数量

// 重写run方法,把线程任务封装进去

public void run() {

for (int i = 0; i < 100; i++) {

if (number > 0) {

try {

Thread.sleep(100); // 让线程休眠0.1秒再运行,相当于网络出现0.1秒延迟

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()

+ "吃了第" + number-- + "个苹果");

}

}

}

}

// 测试类

public class SafeRunnableDemo {

public static void main(String[] args) {

// 创建线程对象

SafeRunnable s = new SafeRunnable();

// 创建Thread对象把线程对象当作参数参数传过去,开启线程

new Thread(s,"xx").start();

new Thread(s,"yy").start();

new Thread(s,"zz").start();

}

}

解决思路:在一个线程执行该任务的时候其他的线程不能来打扰,也就是设置一个同步锁,比如说A线程进入同步锁进行操作的时候,B和C以及其他的线程只能在外面等着,A操作结束,释放同步锁,A,B,C以及其他线程才会有机会去抢同步锁(A线程只要没有执行完所有任务,同样进入争夺同步锁的行列,谁获得同步锁,谁才能执行代码)

同步代码块

同步锁:

对于非static方法,同步锁就是this

对于static方法,同步锁就是当前方法所在类的字节码对象(.class文件)

public class SafeRunnable implements Runnable {

private int number = 100; // 苹果数量

// 重写run方法,把线程任务封装进去

public void run() {

for (int i = 0; i < 100; i++) {

/*

加同步锁

java允许使用任何对象作为同步监听对象

一般我们把当前并发访问的共同资源作为同步监听对象

这里的SafeRunnable对象拥有三个线程共同的资源,

而且只有一份,所以可以用来做同步锁

*/

synchronized (this) { // 加同步锁

if (number > 0) {

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()

+ "吃了第" + number-- + "个苹果");

}

}

}

}

}

同步方法

public class SafeRunnable implements Runnable {

private int number = 100; // 苹果数量

// 重写run方法,把线程任务封装进去

public void run() {

for (int i = 0; i < 100; i++) {

doWork();

}

}

// synchronized修饰的方法是同步方法

synchronized public void doWork() {

if (number > 0) {

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()

+ "吃了第" + number-- + "个苹果");

}

}

}

synchronized的优劣势

好处: 保证了多线程并发访问时的同步操作,避免线程的安全性问题

缺点: 使用synchronized的方法/代码块的性能要低一些

建议: 尽量减小synchronized的作用域

点赞
收藏
评论区
推荐文章
DevOpSec DevOpSec
4年前
python多线程原理和详解(一)
python多线程原理和详解线程概念1.线程是什么?线程也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位。线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。
Wesley13 Wesley13
3年前
java多线程大汇总,线程与进程,线程调度,并发与并行,创建线程方式,线程生命周期,线程安全,线程通信,线程池
1.线程与进程进程是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间线程1、是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行.一个进程最少有一个线程2、线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程
lucien-ma lucien-ma
4年前
什么是线程?什么是进程?
Java多线程基础进程和线程的概念应用程序是静态的概念,进程和线程是动态概念,有创建就有销毁,存在也是暂时的,不是永久性的。进程与线程的区别在于进程在运行时拥有独立的内存空间(每个进程所占有的内存都是独立的)多个线程是共享内存空间的,但是每个线程的执行时相互独立的,同时线程必须依赖于进程才能执行,单独的线程是无法执行的,由进程来控制多个线程的执行。
Stella981 Stella981
3年前
Python并发编程之多进程,多线程
基础概念一、进程、程序和线程程序:程序只是一堆代码而已进程:指的是程序的运行过程,是对正在运行程序的一个抽象。进程是一个资源单位线程:每个进程有一个地址空间,而且默认就有一个控制线程。线程才是cpu上的执行单位二、并发与并行无论是并行还是并发,在用户看来都是'同时'运
Wesley13 Wesley13
3年前
Java并发基础
一、多线程基础知识1.进程和线程进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。线程:进程内部的一个独立执行单元;一个进程可以同时并发的运行多个线程,
Stella981 Stella981
3年前
Android IPC(跨进程通信)之AIDL
AndroidIPC(跨进程通信)之AIDLIPC——跨进程通信,是指两个进程之间的数据交换过程。在说IPC的同时我们要知道什么是进程,什么是线程。线程是CPU调度的最小单元,进程可以理解为一个程序或者一个应用。一个进程中可以运行多个线程,而在Android程序中有一个主线程,也叫UI线程。在And
Wesley13 Wesley13
3年前
Java多线程系列(1)
本章主要内容有:1.线程进程的区别2.线程的生命周期3.Java内存模型原子性,可见性及有序性4.线程池及Java实现1.线程进程的区别线程:程序运行的最小单位进程:资源分配的最小单位一个进程可以有多个线程,多个线程共享进程里面的数据线程间通讯相对更加方便,进程间通讯需要通过IPC(
Stella981 Stella981
3年前
300 行代码带你秒懂 Java 多线程!
  线程的概念,百度是这样解说的:  线程(英语:Thread)是操作体系可以进行运算调度的最小单位。它被包括在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一次序的操控流,一个进程中可以并发多个线程,每条线程并行履行不同的使命。在UnixSystemV及SunOS中也被称为轻量进程(LightweightProcesses),但轻量进程
Stella981 Stella981
3年前
Python 浅析线程(threading模块)和进程(process)
    线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务进程与线程什么是线程(threading)?Athreadisanexecutioncontext,whichisall
Wesley13 Wesley13
3年前
Java多线程介绍
1\.线程概述1.1线程和进程进程是处于运行过程中的程序,并且具有一定的独立功能并发性:同一个时刻只能有一条指令执行,但多个进程指令被快速轮换执行并行:多条指令在多个处理器上同时执行线程是进程的执行单元1.2多
深入浅出线程池 | 京东云技术团队
一、线程1、什么是线程线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。2、如何创建线程2.1、JAVA中
codeseeker
codeseeker
Lv1
万里归船弄长笛,此心吾与白鸥盟。
文章
4
粉丝
0
获赞
0