多线程
进程和线程
什么是进程
- 我们电脑中的每一个正在运行的程序都是一个进程,程序运行时系统就会创建一个进程,并为它分配资源。
- 线程:线程是一条执行路径,是程序执行时的最小单位,它是进程的一个执行流,比如qq可以开多个窗口,和多个人聊天,每个窗口就是一个线程。
进程和线程的关系
一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
java中创建线程
继承Thread类
在java中,创建线程有3种方式。
1.继承Thread类,并且重写run方法,Thread类中的方法不是抽象方法,Thread类也不是抽象类
MyThread类继承了Thread之后,他就是一个线程类。要让线程启动。调用线程的start方法。
class MyThread extends Thread{
@Override
public void run() {
System.out.println("重写的run方法");
System.out.println(2);
}
}
public class Ch01 {
public static void main(String[] args) {
System.out.println(1);
MyThread myThread=new MyThread();
//当调用start方法启动一个线程时会执行重写的run方法的代码
//调用的是start,执行的是run,为什么不直接调run
myThread.start();
//普通的对象调方法,没有启动线程
//myThread.run();
//线程的优先级,概率问题,做不到百分百
//90%会先跑主方法,10%的几率会跑MyThread中的run方法
System.out.println(3);
System.out.println(4);
}
}
输出结果
1
3
4
重写的run方法
2
2.实现Runnable接口
创建类实现Runnable接口,重写run()方法
class MyThread2 implements Runnable{
@Override
public void run() {
System.out.println(2);
}
}
public class Ch02 {
public static void main(String[] args) {
MyThread2 run=new MyThread2();
//如果要想让线程启动,必须调用Thread类中的start方法
//问题:实现了Runnable接口,找不到start方法了\
//可以创建Thread对象,参数中传入Runnable接口或其实现子类
Thread t=new Thread(run);
t.start();
}
}
实现Callable接口
创建类实现Callable接口,重写call()方法
class MyThread03 implements Callable<String>{
@Override
public String call() throws Exception {//
System.out.println("2");
return "call方法的返回值";
}
}
public class Ch04 {
public static void main(String[] args) {
//Callable-->FutureTask-->RunnableFuture-->Runnable-->Thread
System.out.println(1);
FutureTask<String> futureTask=new FutureTask<>(new MyThread03());
new Thread(futureTask).start();
System.out.println(3);
System.out.println(4);
}
}
守护线程
守护线程
java中提供两种类型的线程
1.用户线程
2.守护程序线程
守护线程为用户线程提供服务,仅在用户线程运行时才需要。
守护线程对于后台支持任务非常有用。
垃圾回收,大多是jvm线程其实都是守护线程。
public class Ch05 extends Thread{
public static void main(String[] args) {
Ch05 ch05=new Ch05();
ch05.setDaemon(true);//设置为true则为守护线程
}
}
生命周期
NEW:这个状态主要是线程未被start()调用执行
RUNNABLE:线程正在JVM中被执行,等待来自操作系统的调度
BLOCKED:阻塞,因为某些原因不能立即执行,需要挂起等待。
WAITING:无限期等待。Object。如果没有唤醒,则一直等待。
TIME_WAITING:有限期等待,线程等待一个指定的时间
TERMINATED:终止线程的状态,线程已经执行完毕。
等待和阻塞这两个概念有点像,阻塞因为外部原因,需要等待
等待一般是主动调用方法,发起主动的等待,等待还可以传入参数,来确定等待时间。
CPU多核缓存结构
CPU缓存为了提高程序运行的性能,现在CPU在很多方面对程序进行优化
CPU处理速度最快,内存次之,硬盘速度最低。
在CPU处理内存数据的时,如果内存运行速度太慢,就会拖累cpu的速度,为了解决这样的问题,CPU设计了多级的缓存策略。
CPU分为三级缓存:每个CPU都有L1,L2缓存,但是L3缓存是多级公用的。
CPU查找数据时,CPU->L1->L2->L3
从CPU到内存,60~80纳秒
从CPU到L3,15纳秒
从CPU到L2,3纳秒
从CPU到L1,1纳秒
寄存器,0.3纳秒
进一步优化,CPU每次读取一个数据,读取的是与它相邻的64个字节的数据
【缓存行】
英特尔提出了一个MESI协议
1.修改态,此缓存被动过,内容与主内存中不同,为此缓存专有
2.专有态,此缓存与主内存一致,但是其他CPU中没有
3.共享态,此缓存与主内存一致,其他的缓存也有
4.无效态,此缓存无效,需要从主内存中重新读取
线程安全的实现方法
线程安全的实现方法
1.保证数据不可变
一切不可变的对象一定时线程安全的
对象方法的事项方法的调用者,不需要在进行任何的线程安全的保障的措施
比如说final关键字修饰的基本数据类型,字符串
只要一个不可变的对象它能够正确的创建出来,外部的可见状态永远都不会改变
2.互斥同步,加锁。【悲观锁】
3.非阻塞同步【无锁编程】,自旋,我们会用cas来实现这种非阻塞状态
4.无同步方案:多个线程共享数据,这些数据又可以在单独的线程中计算的出结果,我们能可以把共享数据的可见
范围限制在一个线程之内,这样就无需同步了,把共享的数据拿出来,我用我的,你用你的,从而保证线程的安全
ThreadLocal