目录
一、JAVA各种锁
1、乐观锁
含义:认为当前线程在处理数据时,没有其他数据来改变数据,故一开始不加锁。
实现方式:
1)CAS(比较并替换)
2)版本号控制
2、悲观锁
含义:认为当前线程在处理数据时会有其他线程同时来改变数据,一开始会加锁。
实现方法:synchronized、lock
3、重量级锁
一个线程自旋多圈(默认为15次),线程就进入等待状态,这个线程看待锁对象就是重量级锁。
4、轻量级锁
一个线程运行到一个以A对象为锁的同步代码块,已经有线程获取了锁对象的权限,当前线程就要等待获取锁对象的权限,这时线程就会循环获取锁对象的同意,这是线程看待所对象就是一个轻量级锁。
5、偏向锁
含义:线程进入A对象为锁的同步代码块,内部又进入以A对象为锁的同步代码块,该线程看待这个A锁对象就是偏向锁。
6、可重入锁
线程执行同步代码块,已经获取锁对象的权限,内部在执行到该对象为锁的同步代码块,就不需要再获取该对象的权限了,java中都是可重入锁。
二、JAVA方法
1、wait和notify、notifyAll
看下面代码理解这些方法:
static Object obj=new Object();
static Runnable ra=()->{
synchronized (obj){
System.out.println("A----进入方法");
try {
// Thread.sleep(3000);
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A----结束方法");
}
};
static Runnable rb=()->{
synchronized (obj){
System.out.println("B----进入方法");
System.out.println("B----结束方法");
obj.notify();
}
};
public static void main(String[] args) {
Thread a=new Thread(ra);
Thread b=new Thread(rb);
a.start();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
b.start();
}
最终结果为:
2、stop
用于立即停止线程的执行。这个方法已经被标记为废弃(deprecated),因为它可能会导致一些资源无法正常释放,从而引发资源泄露或其他不可预测的行为。
方法会立即终止线程,不考虑线程当前正在执行的操作。这可能导致线程在执行中间被中断,从而留下未完成的任务或未释放的资源。
3、interrupt
Thread
类的一个方法,用于中断线程。它不会立即停止线程,而是设置线程的中断状态。线程可以通过检查中断状态来决定何时安全地停止自己。
interrupt()
方法不会立即终止线程,而是设置线程的中断状态。线程可以在合适的位置检查中断状态,然后优雅地停止自己。
//结束线程
//1、stop
//2、interrupt 标记此线程可中断,isInterrupted判断是否可中断
//如果为true 就可以抛异常或结束代码
//3、借鉴interrupt方法的作用,自定义一个标记变量,实现判断中断效果
public static void main(String[] args){
Runnable run=new EasyD()::test;
Thread t=new Thread(run);
t.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//这个方法即将被取消
// t.stop();
//此方法不能直接结束线程,执行此方法,只是在线程对象中记录线程的状态是
//可以被中断的,在线程执行体(run)内部,可以使用方法获取线程是否可中断
//如果获取可中断的结果,就可以跳出循环获取抛出异常中断此线程
t.interrupt();
}
public void test(){
while(true){
System.out.println("A----------------");
if(Thread.currentThread().isInterrupted()){
//isInterrupted()返回的是true,说明此线程对象在外部已经执行了
//interrupt方法
break;
}
}
}
4、java如何加锁
1)传统锁
//Lock
ReentrantLock lock=new ReentrantLock();
public void test(){
//加锁
//lock.lock();
while(!lock.tryLock()){ //tryLock尝试获取锁对象 获取到返回true 更加灵活
System.out.println(Thread.currentThread().getName()
+"尝试获取锁对象");
}
for(int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()
+"---"+i);
}
//解锁
lock.unlock();
}
public static void main(String[] args) {
Runnable run=new EasyE()::test;
Thread a=new Thread(run);
Thread b=new Thread(run);
a.start();
b.start();
}
2)读写锁
//读写锁
ReentrantReadWriteLock lockbox=new ReentrantReadWriteLock();
public void testRead(){
Lock readLock=lockbox.readLock();
readLock.lock();
for(int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+
"----"+i);
}
readLock.unlock();
}
public void testWrite(){
Lock writeLock=lockbox.writeLock();
writeLock.lock();
for(int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+
"----"+i);
}
writeLock.unlock();
}
public static void main(String[] args) {
EasyF easyF=new EasyF();
Runnable run=easyF::testRead;
Runnable runW=easyF::testWrite;
Thread a=new Thread(run);
Thread b=new Thread(run);
Thread c=new Thread(run);
Thread w=new Thread(runW);
Thread w1=new Thread(runW);
w.setName("Write");
w1.setName("Write1");
a.start();
b.start();
c.start();
w.start();
w1.start();
//写锁 是一个独占锁 其他线程获取不到读锁和写锁
//读锁 共享锁 只能对读操作共享
}
上面代码在写锁过程中是不会有其他操作的,在读锁过程中可能有多个读锁交叉运行。
三、线程池
1、什么是线程池?
线程池本质是一个容器,可以实现线程的重用,提高对线程的利用率。
传统是每次执行一个任务时就创建一个线程,线程执行完任务就自己销毁。
而线程池是每次执行任务时就从线程池取出一个空闲线程执行任务,执行完任务后线程并不会销毁,而是返回线程池,以供执行下次任务时再使用。
2、线程池的作用?
1)在线程安全的条件下提高执行效率。
2)减少线程创建和线程销毁。
3)提高程序的可维护性和可扩展性。
3、如何创建线程池?
1)创建线程池
ExecutorService es=new ThreadPoolExecutor(5,8,1
, TimeUnit.MINUTES, new LinkedBlockingQueue(),Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
2)线程池执行任务
//执行任务
//Runnable 重写run方法
//Callable 重写call方法
Runnable run=()->{
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"---Run");
};
Callable<String> call=()->{
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName()+"---Call");
return "Easy";
};
//execute 只能执行Runnable 没有返回值
// es.execute(run);
//submit方法 可以处理Runnable、Callable
//返回Future Future中有一个get方法可以获取执行结果,同时get方法会挂起并等待线程
//执行完毕
//submit处理Runnable,返回的是null值,Callable返回的值就是什么值
// Future futureRun=es.submit(run);
Future futureCall=es.submit(call);
try {
System.out.println("-----------------------------");
Object obj=futureCall.get();
System.out.println(obj);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
4、线程池的7个参数?
//1、核心线程数量
//2、最大线程数量
//3、存活时间
//4、时间单位
//5、工作队列
//6、线程工厂
//7、回绝策略
5、线程池的工作原理?
1)开始执行一个线程任务时,线程池会分配一个空闲线程去执行任务;
2)当任务过多且核心线程都已经分配任务时,此时若再执行任务,会将任务放到工作队列中去,若有核心线程运行完空闲时,逐个处理工作队列中的任务;
3)若有新的任务且核心线程和工作队列都已经满了的时候,会启用新的线程(但线程总数不能超过最大线程数量)去执行;
4)若有新的任务且此时所有线程(最大线程数量)和工作队列都运行时,会执行回绝策略,回绝策略有四种,如:抛出异常、不做任何反应、让发布者执行、删除工作队列头部的任务并插入新任务。
5)如果有线程空闲,并且线程数量超过核心线程数,就对该线程进行计时,达到最大存活时间, 就将该线程消亡掉,直到池中线程数量达到核心线程数