Java高级知识复盘之线程创建方式,常用方法,安全问题
线程基础知识-名词
线程创建方式
线程常用方法
线程安全问题
1 技术简介
1.1 进程
(1)一个程序就是一个进程
(2)进程是资源分配的基本单位
操作系统分配资源
操作系统:operating system os
资源:CPU、内存等
(3)一个进程中包含多个线程 多线程
1.2 线程
(1)真正执行任务的是线程
(2)线程是任务调度的基本单位
(3)多个线程共享进程中的资源
1.3 并发
(1)同时发生
多个请求同时发生了
(2)高并发:
大量的同时发生
双十一
服务器支持高并发
1.4 并行
(1)同时进行
(2)并行发生前提:
多核CPU
(3)单核CPU:
同一时刻,只执行一个任务
(4)案例:
40个任务 4核
每核执行10个任务,切换执行
1.5 串行
(1)任务是一个接一个的执行
(2)单核CPU:
串行化执行多个任务
(3)宏观并行、微观串行
1.6 同步
(1)一个任务的开始要等待另外一个任务的结束 ***
(2)举例:
接力赛
1.7 异步
(1)一个任务的开始不用等待另外一个任务的结束
(2)举例:
有道
网易云 搜索任务|播放任务
2 线程创建(重要)
(1)创建一个类,继承Thread类
(2)实现Runnable接口
(3)实现Callable接口
(4)线程池
2.1 创建方式一
2.1.1
package com.javasm.thread.create;
public class Emp extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程执行任务: " + i);
}
}
}
2.1.2
package com.javasm.thread.create;
public class CreateExercise {
public static void main(String[] args) {
}
private static void method1() {
System.out.println("main start");
Emp emp = new Emp();
emp.start();
System.out.println("main end");
}
}
2.2 创建方式二
package com.javasm.thread.create;
public class CreateExercise {
public static void main(String[] args) {
}
private static void method2() {
System.out.println("main start");
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("threadA执行任务:"+i);
}
}
});
Thread threadB = new Thread(()->{
for (int i = 0; i < 10; i++) {
System.out.println("threadB执行任务:"+i);
}
});
threadA.start();
threadB.start();
System.out.println("main end");
}
}
2.3 创建方式三
package com.javasm.thread.create;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CreateExercise {
public static void main(String[] args) {
method3();
}
private static void method3() {
System.out.println("main start");
FutureTask<Integer> futureTask = new FutureTask<Integer>(new Callable<Integer>() {
int sum = 0;
@Override
public Integer call() throws Exception {
for (int i = 0; i < 10; i++) {
sum += i;
}
return sum;
}
});
Thread threadA = new Thread(futureTask);
Integer count = 0;
try {
count = futureTask.get();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
System.out.println("数字之和:" + count);
System.out.println("main end");
}
}
3 线程方法
3.1 基础介绍
(1)run():定义线程任务
(2)start():启动线程,告知os调度器准备好了,可以被调度了
(3)getState():线程状态
(4)getName():线程名称
(5)getId():线程ID
(6)getPriority():线程优先级,参考值->优先级值比较高的线程有可能优先执行
(7)activeCount():统计活着的线程个数
(8)sleep():让线程休眠 ***
(9)join():等某线程执行完毕 ***
3.2 案例
package com.javasm.thread.methods;
import java.util.concurrent.TimeUnit;
public class MethodsExercise {
public static void main(String[] args) {
System.out.println("main start");
Thread threadDemoA = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "执行任务:" + i);
}
}
}, "threadA");
threadDemoA.start();
try {
threadDemoA.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("main end");
}
}
4 多线程数据安全
4.1 案例
4.1.1 Bank
package com.javasm.thread.safe;
public class Bank {
private double balance;
public void save(double money) {
balance += money;
}
public void draw(double money) {
balance -= money;
}
public double getBalance() {
return balance;
}
}
4.1.2
package com.javasm.thread.safe;
import java.util.concurrent.TimeUnit;
public class SafeExercise {
public static void main(String[] args) {
method2();
}
private static void method2() {
Bank bank = new Bank();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
bank.save(100);
}
System.out.println(Thread.currentThread().getName()+"执行结束");
}
}, "threadA");
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
bank.draw(100);
}
System.out.println(Thread.currentThread().getName()+"执行结束");
}
}, "threadB");
threadA.start();
threadB.start();
try {
threadA.join();
threadB.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("balance: " + bank.getBalance());
}
}
4.2 出现问题
(1)程序中引入多线程,可以提高程序的执行效率
(2)当多个线程同时操作共享主存中的变量值时,会出现数据不安全问题
(3)当某线程执行任务时,执行期间被打断
4.3 JMM
(1)JMM:Java Memory Model,Java内存模型 一套规范
(2)JMM给线程划分了工作内存和共享主存
(3)JMM规范:
A.每个线程都有自己的工作空间 工作内存
B.线程的局部变量都在各自的工作内存中
C.各个线程都可访问共享主存中的值,但不能直接修改主存中的值
D.每个线程的工作空间都是独立的,是互不影响的
4.4 synchronized(重要)
(1)做到原子性:
原子性:一系列操作要么全部执行要么全部都不执行
该操作在执行期间不被打断
(2)synchronized修饰方法->同步方法
同步:一个任务的开始必须要等待另外一个任务的结束
(3)底层机制:
每一个对象都有唯一的一把对象锁
同一时刻,一把对象锁只能被一个线程获取到
(4)使用位置:
普通方法:
方法声明处使用synchronized:
public synchronized void save(double money) {}
线程要想执行方法内容,就必须获取到对象的对象锁;
当该方法中的内容执行完毕,释放对象锁;->对象锁被其他线程获取到
锁粒度较大 整个方法
数据安全 性能低
同步代码块:
synchronized (this) {
balance += money;
}
线程要想执行局部代码块中的内容,需要先获取到对象锁;
当局部代码块中的代码执行完毕,释放对象锁;
锁粒度较小 局部代码块
数据安全 性能高
静态方法:
方法声明处使用synchronized:
线程想执行该方法代码,获取到类对象的对象锁
局部代码块使用synchronized:
不能使用this
(5)注意事项:
内置锁 | 排他锁(互斥锁) | 可重入锁(不用重复获取对象锁);
遇到异常时,释放对象锁
(6)扩展:
sleep方法:
线程不会释放对象锁
内部同步:
Bank:使用synchronized关键字
外部同步:
Bank:不使用synchronized关键字
测试类:使用synchronized关键字
4.5 ReentrantLock
4.5.1 内容描述
(1)显示锁
(2)ReentrantLock类:
加锁:lock()
放锁:unlock()
(3)规范使用:
借助于异常块使用
把unlock的调用放到finally中
(4)显示锁 | 排他锁(互斥锁) | 可重入锁
4.5.2 案例
package com.javasm.thread.safe;
import java.util.concurrent.locks.ReentrantLock;
public class Bank3 {
private double balance;
private ReentrantLock reentrantLock = new ReentrantLock();
public void save(double money) {
System.out.println(Thread.currentThread().getName() + "进入save方法");
reentrantLock.lock();
try {
balance += money;
System.out.println(1 / 0);
} finally {
reentrantLock.unlock();
}
System.out.println(Thread.currentThread().getName() + "执行结束");
}
public void draw(double money) {
reentrantLock.lock();
balance -= money;
reentrantLock.unlock();
}
public double getBalance() {
return balance;
}
}
4.6 synchronized和ReentrantLock
(1)synchronized 内置锁
(2)ReentrantLock 显示锁 1.5
(3)性能基本差不多
(4)比如:集合类 synchronized
5 线程安全类
(1)内部同步:
Bank save() draw()
(2)外部同步:
Bank 没有做任何同步 外部同步
(3)集合类:
线程安全:vector、hashtable
线程非安全:ArrayList、HashMap
(4)原子性类:
Atomicxxx
无锁编程思想:无synchronized和ReentrantLock
CAS:Compare And Swap,比较并交换
线程将更改后值刷新到主存之前,都会去主存中获取最新值;
比较;
即使线程在操作过程被挂起,不影响操作
synchronized:方法声明处 | 局部代码块
ReentrantLock:比较宽泛
Atomicxxx:比较局限 AtomicInteger或...