0.背景知识点:
1.什么是上下文切换:
前线程使用完时间片后,就会处于就绪状态并让出 CPU,让其他线程占用,这就是上下文切换,从当前线程的上下文切换到了其他线程.
2.什么是线程死锁:
线程a持有资源1.申请2
线程b持2,申请1
3.线程死锁的必备要素
1.互斥:线程对所分配的资源的排他性控制
2.不可剥夺:a在使用1的时候,不能被其他线程夺走,只能自己释放
3.请求保持:a请求2不得被阻塞的时候,保持对1的不释放
4.循环等待:
指在发生死锁时,必然存在一个线程请求资源的环形链,即线程集合 {T0,T1,T2,…Tn}中的 T0 正在等待一个 T1 占用的资源,T1 正在等待 T2 占用的资源,以此类推,Tn 正在等待己被 T0 占用的资源。
5.线程的主要方法:
1.start()用来启动一个线程
2.run()
Thread threadB = new Thread(new Runnable() { //创建线程 1 @Override public void run() {
3.sleep方法
sleep(long millis) //参数为毫秒
sleep(long millis,int nanoseconds) //第一参数为毫秒,第二个参数为纳秒
4.yield方法线程让步
class ThreadA extends Thread { public void run() { for (int i = 0; i < 10; i++) { System.out.println("ThreadA--" + i); Thread.yield(); } } }
Thread中yield方法 - 劈天造陆 - 博客园 (cnblogs.com)
5.interrupt方法的
6.interrupted方法 此方法首先调用isInterrupted方法,而isInterrupted方法是一个重载的native方法
用来测试线程是否已经中断
public boolean isInterrupted() {
return isInterrupted(false);
}
7.join方法有三个重载版本:
join()
join(long millis) //参数为毫秒
join(long millis,int nanoseconds) //第一参数为毫秒,第二个参数为纳秒
eg: thread1.join();
8.stop()弃用
9.destory()弃用
10.对属性进行设置的方法
1)getId
用来得到线程ID
2)getName和setName
用来得到或者设置线程名称。
3)getPriority和setPriority
用来获取和设置线程优先级。
4)setDaemon和isDaemon
用来设置线程是否成为守护线程和判断线程是否是守护线程。
参考:java.lang.Thread类详解 - Ryan520 - 博客园 (cnblogs.com)
6.守护线程
daemon 线程(守护线程〉和 user 线程(用户线程)。
守护线程定义:所谓守护线程,是指在程序运行的时候在后台提供一种通用服务的线程。比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。
而 Java 中的守护线程是 JVM 级别的,当 JVM 中无任何用户进程时,守护进程销毁,JVM 退出,程序终止
public class DemoTest {
public static void main(String[] args) throws InterruptedException {
Thread threadOne = new Thread(new Runnable() {
@Override
public void run() {
//代码执行逻辑
}
});
threadOne.setDaemon(true); //设置threadOne为守护线程
threadOne. start();
}
}
//thread.setDaemon (true) 必须在 thread.start () 之前设置,否则会跑出一个 //llegalThreadStateException 异常。你不能把正在运行的常规线程设置为守护线程;
//在 Daemon 线程中产生的新线程也是 Daemon 的;
//守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生
//中断
7.线程的六个state
public enum State{
NEW,
RUNNABLE,
BLOCKED, //处于blocked状态的线程还是会去竞争锁的,cpu有时间,竞争到,执行
WAITING, //不去竞争锁,被动的等通知
TIMED_WAITING,//超时等待
TERMINATED,
}
1.ThreadLocal
ThreadLocal 是 JDK 包提供的,它提供了线程本地变量,也就是如果你创建了一个 ThreadLocal 变量,那么访问这个变量的每个线程都会有这个变量的一个本地副本。当多个线程操作这个变量时,实际操作的是自己本地内存里面的变量,从而避免了线程安全问题。
方法
1.set
ThreadLocal<String> localVariable = new ThreadLocal<> () ;
localVariable.set("Hello World");
set 方法可以设置任何类型的值,
2.get
localVariable.set("Hello World");
System.out.println(localVariable.get());
3.ThreadLocal remove
localVariable.set("Hello World");
System.out.println(localVariable.get());
localVariable.remove();
System.out.println(localVariable.get());
//清除成功之后没有变量值
多线程下的 ThreadLocal
static ThreadLocal<String> local = new ThreadLocal<>();
public static void main(String[] args){
Thread threadOne = new Thread(new Runnable() {
Thread threadOne = new Thread(new Runnable() {
@Override public void run() {
Thread threadTwo = new Thread(new Runnable() {
@Override public void run() {
2.锁
0.多线程锁的分类:
1.悲观锁
悲观锁指对数据被外界修改持保守态度,认为数据很容易就会被其他线程修改(很悲观),所以在数据被处理前先对数据进行加锁,并在整个数据处理过程中,使数据处于锁定状态。
Java 中的 synchronized 关键字就是一种悲观锁
2.乐观锁:
乐观锁是相对悲观锁来说的,它认为数据在一般情况下不会造成冲突,所以在访问记录前不会加排它锁,而是在进行数据提交更新的时候,才会正式对数据冲突与否进行检测。
CAS 原理即是乐观锁技术
Compare And Swap 比较和交换
CAS 主要包含三个操作数,内存位置 V,进行比较的原值 A,和新值 B。
当位置 V 的值与 A 相等时,CAS 才会通过原子方式用新值 B 来更新 V,
3.公平锁和非公平锁
是否先到先得
ReentrantLock:ReentrantLock 提供了公平和非公平锁的实现。
4.独占锁和共享锁
独占锁:保证任何时候都只有一个线程能得到锁,ReentrantLock 就是以独占锁方式实现的。(是一种悲观锁)
共享锁:则可以同时由多个线程持有,例如 ReadWriteLock 读写锁,它允许一个资源可以被多线程同时进行读操作。
5.自旋锁
资源被占用的时候不马上阻塞自己,而是多次尝试获取,默认次数是10
锁1: synchronized (原子,顺序,内存可见)
1.互斥条件,用同一把锁
2.静态方法锁类的所有对象
3.同一线程支持重入
4.修饰举例(方法,静态方法,代码块)
1).修饰实例方法
public synchronized void increase() throws InterruptedException {
sleep(1000);
2).静态方法
public static synchronized void increase() throws InterruptedException {
System.out.println(Thread.currentThread().getName() + "获取到锁,其他线程在我执行完毕之前,不可进入。" );
sleep(1000);
count++;
System.out.println(Thread.currentThread().getName() + ": " + count);
}
3).代码块
static final Object objectLock = new Object(); //创建一个对象锁
public static void increase() throws InterruptedException {
System.out.println(Thread.currentThread().getName() + "获取到锁,其他线程在我执行完毕之前,不可进入。" );
synchronized (objectLock) {
sleep(1000);
count++;
System.out.println(Thread.currentThread().getName() + ": " + count);
}
}
锁2: 关键字vloatile(保证变量的内存可见性,禁止指令重排序)
private volatile boolean flag=false;
public class Student { private String name; public synchronized String getName() { return name; } public synchronized void setName(String name) { this.name = name; } } public class Student { private volatile String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
此时等价
详细讲解:
Java volatile关键字最全总结:原理剖析与实例讲解(简单易懂)_夏日清风-CSDN博客_java volatile
不能用于非原子操作,eg:i++
锁3:lock类
1.lock类引入必要性,因为synchronized的顺序性,全部线程对一个资源进行读操作时,其他线程会进行等待
2.实现接口:
public interface Lock {
void lock(); //获取锁
void lockInterruptibly() throws InterruptedException;
//前线程未被中断,则获取锁
boolean tryLock(); //获取空闲锁
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();//如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁;
Condition newCondition(); //如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁;
}