多线程基础知识
一、概念:
什么是进程?
什么是线程?
什么是纤程/协程?
1、什么是进程
进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用
程序运行的载体。进程是一种抽象的概念,从来没有统一的标准定义。进程一般由程序,数据集合和进程控制块三部分组成。程序用于描述进程要完成的功能,是控制进程执行的指令集;数据集合是程序在执行时所需要的数据和工作区;程序控制块包含进程的描述信息和控制信息,是进程存在的唯一标志。
怎么理解?
操作系统运行可执行文件时,会先把程序加载到内存中,此时就会启动一个进程,这个进程会对资源进行分配,包括端口,文件描述符等等…
2、什么是线程
程序执行以线程方式执行。线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位。一个进程可以有一个或多个线程,各个线程之间共享程序的内存空间(也就是所在进程的内存空间)。一个标准的线程由线程ID,当前指令指针PC,寄存器和堆栈组成。而进程由内存空间(代码,数据,进程空间,打开的文件)和一个或多个线程组成
二、线程切换
1、单核CPU设置多线程是否有意义
针对IO密集型可以
2、线程数是不是越大越好
线程切换也需要时间、所以并不是越大越好
3、工作线程数(线程池中线程数量)设置为多少合适
N t h r e a d = U c p u ∗ N c p u ∗ ( 1 + W / C ) Nthread = Ucpu * Ncpu * (1 + W/C) Nthread=Ucpu∗Ncpu∗(1+W/C)
Ucpu: 期望cpu 使用率
Ncpu: cpu核数
W : 等待
C :计算
W/C 需要部署后进行测试
public class T01_MutiVSSingle_ContextSwitch {
private static double[] nums = new double[100000000];
private static Random r = new Random();
private static DecimalFormat df = new DecimalFormat("0.00");
static {
for (int i = 0; i < nums.length; i++) {
nums[i] = r.nextDouble();
}
}
private static void m1(){
long start = System.currentTimeMillis();
double result = 0.00;
for (int i = 0; i < nums.length; i++) {
result+= nums[i];
}
long end = System.currentTimeMillis();
System.out.println("m1:" + (end - start) + " result = "+ df.format(result));
}
static double result1= 0.00 , result2= 0.00 ,result = 0.00;
private static void m2() throws InterruptedException {
Thread thread1 = new Thread(() -> {
for (int i = 0; i < nums.length / 2; i++) {
result1 += nums[i];
}
});
Thread thread2 = new Thread(() -> {
for (int i = nums.length / 2; i < nums.length; i++) {
result2 += nums[i];
}
});
long start = System.currentTimeMillis();
thread1.start();
thread2.start();
thread1.join();
thread2.join();
result = result1 + result2;
long end = System.currentTimeMillis();
System.out.println("m2:" + (end - start) + " result = "+ df.format(result));
}
private static void m3() throws InterruptedException {
final int threadCount = 10000;
Thread[] threads = new Thread[threadCount];
double[] results = new double[threadCount];
final int segmentCount = nums.length / threadCount;
CountDownLatch latch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
int m = i;
threads[i] = new Thread(()->{
for (int j = m*segmentCount; j < (m+1)*segmentCount && j< nums.length ; j++) {
results[m] += nums[j];
}
latch.countDown();
});
}
double resultM3 = 0.00;
long start = System.currentTimeMillis();
for (Thread thread:
threads) {
thread.start();
}
latch.await();
for (int i = 0; i < results.length; i++) {
resultM3 += results[i];
}
long end = System.currentTimeMillis();
System.out.println("m3:" + (end - start) + " result = "+ df.format(resultM3));
}
public static void main(String[] args) throws InterruptedException {
m1();
m2();
m3();
}
}
线程基础操作
如何创建线程
1、继承Thread,重写Run方法
2、实现Runable
3、Lambda表达式
4、使用线程池
5、如果需要拿到线程中的返回值,可以实现Callable 配合Future+线程池 或者 futureTask+thread实现
public class T02_HowToCreateThread {
static class MyThread extends Thread{
@Override
public void run() {
System.out.println("继承Thread ,重写run方法");
}
}
static class MyRunable implements Runnable{
@Override
public void run() {
System.out.println("实现runable 接口");
}
}
static class MyCall implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("使用call 可以返回参数");
return "success";
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 继承Thread 重写 run 方法
new MyThread().start();
// 实现Runable 更加灵活,可以另外继承类
new Thread(new MyRunable()).start();
//
new Thread(()->{
System.out.println("表达式直接实现");
}).start();
// 线程池创建(不推荐使用Executor创建线程池)
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(()->{
System.out.println("使用线程池创建");
});
// 异步执行,线程执行完成后 放到Future的值
Future<String> f = executorService.submit(new MyCall());
try {
// 阻塞值后,当拿到结果后会继续执行
String s = f.get();
System.out.println(s);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
executorService.shutdown();
FutureTask<String> futureTask = new FutureTask<>(new MyCall());
Thread thread = new Thread(futureTask);
thread.start();
System.out.println("自己启动线程,使用futureTask"+futureTask.get());
}
}
线程状态
New :线程刚刚创建
Runable : 可运行 Ready 、Running
Waiting : 等待唤醒
TIMED WAITING : 隔一段时间后自动唤醒
BLOCKED : 被阻塞,等待锁
TERMINATED:进程结束
public class T03_ThreadState {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
// 执行中 runable
System.out.println("2: 当前状态" + Thread.currentThread().getState());
for (int i = 0; i < 3; i++) {
TimeHelper.sleep(1);
System.out.println("==========");
}
});
// 刚创建,没有调用start new
System.out.println("1: 当前状态" + thread1.getState());
thread1.start();
// 等待线程结束
thread1.join();
// 线程结束
System.out.println("3: 当前状态" + thread1.getState());
// --------------------------------------------------
Thread thread2 = new Thread(() -> {
LockSupport.park();
System.out.println("t2 go ");
TimeHelper.sleep(5);
});
thread2.start();
TimeHelper.sleep(1);
// t2 启动后 park waiting
System.out.println("4 : t2 状态: " + thread2.getState());
LockSupport.unpark(thread2);
TimeHelper.sleep(1);
// t2 unpark 因为在sleep 所以在 Timed_waiting
System.out.println("5 : t2 状态" + thread2.getState());
// ----------------------------------------------------
final Object o = new Object();
Thread thread3 = new Thread(() -> {
synchronized (o) {
System.out.println("t3 得到锁");
}
});
// 立即执行抢锁
new Thread(()->{
synchronized (o){
TimeHelper.sleep(5);
}
}).start();
TimeHelper.sleep(1);
thread3.start();
TimeHelper.sleep(1);
// 加上锁了 所以Blocked
System.out.println("t3 : 状态" + thread3.getState());
// -------------------------------------------------
// 区分
final Lock lock = new ReentrantLock(); // 盲等待 CAS
Thread thread4 = new Thread(() -> {
lock.lock();
System.out.println("t4 得到了锁");
lock.unlock();
});
new Thread(()->{
lock.lock();
TimeHelper.sleep(5);
lock.unlock();
}).start();
TimeHelper.sleep(1);
thread4.start();
TimeHelper.sleep(1);
// WAITING
System.out.println("t4 :状态" + thread4.getState());
//------------------------------------------------
Thread thread5= new Thread(() -> {
LockSupport.park();
});
thread5.start();
TimeHelper.sleep(1);
// WAITING
System.out.println("t5 :" + thread5.getState());
LockSupport.unpark(thread5);
}
}
线程打断(Interrupt)
interrupt() : 设置标记位
isInterrupted(): 线程是否被标记过 查询标记位
static interrupted() 判断当前线程是否被标记过 并重置标记位
public class T04_ThreadInterrupt {
static void t1(){
Thread thread = new Thread(() -> {
for (; ; ) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("设置过interrupt");
System.out.println(Thread.currentThread().isInterrupted());
}
}
});
thread.start();
TimeHelper.sleep(2);
thread.interrupt();
}
static void t2(){
Thread thread = new Thread(() -> {
for (; ; ) {
if (Thread.interrupted()) {
System.out.println("设置interrupted");
System.out.println(Thread.interrupted());
}
}
});
thread.start();
TimeHelper.sleep(2);
thread.interrupt();
}
public static void main(String[] args) {
// t1();
t2();
System.out.println("main :" + Thread.interrupted());
}
}
public class T05_Thread_Interrupt_Sleep {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
System.out.println("Thread is interrupted");
// 抛出异常后 java会自动将interrupted 重置为false 结果为false
System.out.println(Thread.currentThread().isInterrupted());
}
});
thread.start();
TimeHelper.sleep(3);
thread.interrupt();
}
}
public class T06_Thread_Synchronized_Interrupt {
/**
* 争抢锁的过程中用interrupt 不会打断争抢锁
* @param args
*/
public static void main(String[] args) {
Object o = new Object();
Thread thread1 = new Thread(() -> {
synchronized (o) {
System.out.println(new Date());
TimeHelper.sleep(10);
}
});
thread1.start();
TimeHelper.sleep(1);
Thread thread2 = new Thread(() -> {
synchronized (o) {
System.out.println(new Date());
System.out.println("t2 end");
}
});
thread2.start();
TimeHelper.sleep(1);
thread2.interrupt();
}
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Thread thread1 = new Thread(() -> {
lock.lock();
TimeHelper.sleep(10);
lock.unlock();
});
thread1.start();
TimeHelper.sleep(1);
Thread thread2 = new Thread(() -> {
lock.lock();
lock.unlock();
System.out.println("t2_end");
});
thread2.start();
TimeHelper.sleep(1);
thread2.interrupt();
}
}
/**
* 如果想对争抢锁用Interrupt 用ReentrantLock ,使用 LockInterruptibly
**/
public class T08_Thread_LockInterruptibly_Interrupt {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Thread thread1 = new Thread(() -> {
lock.lock();
TimeHelper.sleep(10);
lock.unlock();
});
thread1.start();
TimeHelper.sleep(1);
Thread thread2 = new Thread(() -> {
System.out.println("t2_start");
try {
// 可以被打断
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
System.out.println("t2_end");
});
thread2.start();
TimeHelper.sleep(1);
thread2.interrupt();
}
}
如何比较优雅的结束线程
为什么不建议t.stop()
容易产生数据不一致的问题
public class T01_Thread_Stop {
public static void main(String[] args) {
Object o = new Object();
Thread thread1 = new Thread(()->{
// synchronized (o){
// System.out.println("go on");
// TimeHelper.sleep(10);
// }
System.out.println(" thread 01 zhuanqian");
TimeHelper.sleep(10);
System.out.println(" thread 01 shouqian");
});
thread1.start();
// TimeHelper.sleep(1);
//
// Thread thread2 = new Thread(() -> {
// synchronized (o) {
// System.out.println("t2 执行");
// }
// });
//
// thread2.start();
// TimeHelper.sleep(1);
TimeHelper.sleep(2);
thread1.stop();
// thread1.suspend();
// TimeHelper.sleep(1);
// thread1.resume();
}
}
// 无法准确停止
public class T01_Thread_Volatile_Stop {
private static volatile boolean running = true;
public static void main(String[] args) {
Thread thread = new Thread(() -> {
long i = 0L;
while (running) {
i++;
}
System.out.println("end i = ++ " + i);
});
thread.start();
TimeHelper.sleep(1);
running = false;
}
}
// 无法准确停止
public class T01_Thread_Interrupted_Stop {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
while (!Thread.interrupted()) {
System.out.println("jajaj");
}
System.out.println("t end");
});
thread.start();
TimeHelper.sleep(1);
thread.interrupt();
}
}
并发编程
一、三大特性
可见性、有序性、原子性
1、可见性
volatile
public class T001_Visibility {
// 数据更改都会到主存中读取、以及主存刷新
public static /*volatile*/ boolean running = true;
public static void m(){
System.out.println("m start");
while (running){
// println 会触发本地缓存 和 主存的读取
// System.out.println("hello");
}
System.out.println("m end");
}
public static void main(String[] args) {
new Thread(T001_Visibility::m,"t1").start();
TimeHelper.sleep(1);
running = false;
}
}
public class T002_VolatileReference {
private static class A{
// 在这加就可以
/*volatile*/ boolean running = true;
void m(){
System.out.println("m start");
while (running){}
System.out.println("m end");
}
}
// volatile 只修饰了引用类型,不可见
private volatile static A o = new A();
public static void main(String[] args) {
new Thread(o::m,"t1").start();
TimeHelper.sleep(2);
o.running = false;
}
}
三级缓存
读取某一数据时,会同时读取周边的数据。
按块读取(缓存行 cache line)
public class T003_CacheLinePadding {
public static long COUNT = 1000000000L;
private static class T{
// 添加这些后可以保证不会被在同一个缓存行出现
private long p1,p2,p3,p4,p5,p6,p7;
public long x = 0L;
private long p9,p10,p11,p12,p13,p14,p15;
}
public static T[] arr = new T[2];
static {
arr[0] = new T();
arr[1] = new T();
}
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(2);
Thread thread1 = new Thread(() -> {
for (int i = 0; i < COUNT; i++) {
arr[0].x = i;
}
latch.countDown();
});
Thread thread2= new Thread(() -> {
for (int i = 0; i < COUNT; i++) {
arr[0].x = i;
}
latch.countDown();
});
final long start = System.currentTimeMillis();
thread1.start();
thread2.start();
latch.await();
final long end = System.currentTimeMillis();
System.out.println("time " + (end - start));
}
}
@Contended
-XX:-RestrictContended
public class T004_Contended {
public static long COUNT = 1000000000L;
private static class T{
@Contended
public long x = 0L;
}
public static T003_CacheLinePadding.T[] arr = new T003_CacheLinePadding.T[2];
static {
arr[0] = new T003_CacheLinePadding.T();
arr[1] = new T003_CacheLinePadding.T();
}
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(2);
Thread thread1 = new Thread(() -> {
for (int i = 0; i < COUNT; i++) {
arr[0].x = i;
}
latch.countDown();
});
Thread thread2= new Thread(() -> {
for (int i = 0; i < COUNT; i++) {
arr[0].x = i;
}
latch.countDown();
});
final long start = System.currentTimeMillis();
thread1.start();
thread2.start();
latch.await();
final long end = System.currentTimeMillis();
System.out.println("time " + (end - start));
}
}
缓存一致性协议
MESI
Modified 修改
Exclusive 独享
Shared 共享
Invalid 失效
2、有序性
程序是否按照"顺序"执行
public class T005_DisOrder {
private static int x = 0, y = 0;
private static int a = 0, b = 0;
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < Long.MAX_VALUE; i++) {
x = 0;
y = 0;
a = 0;
b = 0;
CountDownLatch countDownLatch = new CountDownLatch(2);
//
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
a = 1;
x = b;
countDownLatch.countDown();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
b = 1;
y = a;
countDownLatch.countDown();
}
});
thread1.start();
thread2.start();
countDownLatch.await();
String result = "第" + i + "次 (" + x + "," + y + ")";
if(x == 0 && y == 0){
System.err.println(result);
break;
}
}
}
}
乱序
为了提升效率
存在条件:
as - if - serial (像是序列化执行)
不影响单线程的最终一致性
public class T006_NoVisibility {
// 可见性
private static volatile boolean ready = false;
private static int number;
private static class ReaderThread extends Thread{
@Override
public void run() {
while (!ready){
Thread.yield();
}
System.out.println(number);
}
}
public static void main(String[] args) throws InterruptedException {
Thread t = new ReaderThread();
t.start();
// 可能会乱序
number = 42;
ready = true;
t.join();
}
}
public class T007_ThisEscape {
public static void main(String[] args) {
Object o = new Object();
}
}
对象创建
0 new #2 <java/lang/Object>
3 dup
4 invokespecial #1 <java/lang/Object.<init> : ()V>
7 astore_1
8 return
申请内存 ,成员变量赋默认值 => 对象构造方法执行,成员变量赋初始值 => 指针指向内存
this对象逸出
public class T007_ThisEscape {
private int num = 8;
public T007_ThisEscape(){
// 可能会输出中间状态0
new Thread(()-> System.out.println(this.num)).start();
}
public static void main(String[] args) {
new T007_ThisEscape();
TimeHelper.sleep(10);
}
}
DCL需不需要加volatile
其实通过上面的描述,这个答案我们已经很清楚了,是需要加上volatile的,因为乱序的存在,可能会在我们判断对象是否为空时,将半初始化的状态认为非空,将半初始化的一个对象返回出去了。
public class T008_DCL {
private static volatile T008_DCL t008_dcl = null;
private T008_DCL(){}
public static T008_DCL getInstance(){
if(t008_dcl == null){
synchronized (T008_DCL.class){
if(t008_dcl == null){
t008_dcl = new T008_DCL();
}
}
}
return t008_dcl;
}
}