线程安全原因:
1.共享数据
2.多条线程操作共享数据
在java中关键字synchronized可以保证同一时刻只有一个线程可以执行某个方法或某个方法块,同时保证一个线程的变化可以被其他线程看到
锁:锁就是一个类似于队列的数据结构,当有多个线程并发访问对象时,如果对象已经被其他线程锁定,那么当前线程会有一个入队的操作,同步操作的实现,需要给对象关联一个互斥体,这个互斥体就可以叫做锁
互斥锁:当存在多个数据操作共享数据时,需要保证有且仅有一个线程在操作共享数据,其他线程必须等该线程处理完数据后才能执行
悲观锁:一个线程加锁后,使得该对象成为该线程的多有对象,其他线程只能悲观锁阻拦在外,无法操作
乐观锁:认为多个线程一个对象的操作不会冲突,所有不加锁,只是在提交更改时验证是否冲突,若冲突则重试知道成功(尝试过程称为自旋)
线程五种状态:
CAS
执行函数:CAS(V,E,N)
先获得原值prev和要改成的新值next,当CAS替换新值不成功时,自旋重新获得原值prev和新值next再试一次
public final int getAndUpdate(IntUnaryOperator updateFunction) {
int prev, next;
do {
prev = get();
next = updateFunction.applyAsInt(prev);
} while (!compareAndSet(prev, next));
return prev;
}
多线程锁
1.无锁
没有对资源锁定,所有线程都能访问并修改资源,但同时只能又一个修改成功,其他线程通过CAS自旋不断尝试
2.偏向锁
偏向第一个加锁线程,该线程不会主动释放偏向锁,只有当其他线程竞争偏向锁才会释放
偏向锁的撤销,当某个时间点没有字节码执行时
a.暂停偏向锁的线程
b.判断对象是否处于被锁定状态
c.i:如果线程不处在活动状态,则将对象头设置成无所状态,并撤销偏向锁
c.ii:如果线程处在活动状态,则升级为轻量级锁状态
3.轻量级锁
当锁时偏向锁时,被第二个线程B访问,则偏向锁会升级为轻量级锁,线程B会以自旋方式获取锁
4.重量级锁
当又一个线程获取锁后,其余所有等待获取该锁的线程处于阻塞状态
当自旋数量达到一定数量时,轻量级锁会升级为重量级锁;
当超过两个线程在自旋等待获取锁,轻量级锁也会升级为重量级锁(阻塞了)
线程池
线程池的五种状态:
1.RUNNING(runing)
表示线程池正在运行中,可以正常的接受并处理任务
2.SHUTDOWN(shutdown)
表示线程池关闭了,不能接受新任务,但是线程池会把阻塞队列中的剩余任务执行完,剩余任务处理完之后们会中断所有线程
3.STOP(stop)
也表示线程关闭了,不能接受新任务,但是并不会处理阻塞队列中剩下的任务,会直接中断线程
4.TIDYING(tidying)
当线程中没有没有工作线程在运行之后就会进入TIDYING状态
5.TERMINATED(terminated)
线程池处于TIDYING状态后,会执行terminated()方法,执行完成后就会进入TERMINATED状态
另外,在ThreadPoolExecutor中terminated()方法是一个空方法,可以自定义线程池重写这个方法,来定义线程池关闭完成需要执行的逻辑
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
Executors类线程池:
代码实例
package com.guor.thread;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Handle implements Runnable {
private String name;
public Handle(String name) {
this.name = "Thread" + name;
}
@Override
public void run() {
System.out.println(name + "-> start time : " + new Date());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "-> end time : " + new Date());
}
public static void main(String[] args) throws InterruptedException {
scheduledTest();
}
//可缓存的线程池
private static void cacheTest() {
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
Handle handle = new Handle(String.valueOf(i));
newCachedThreadPool.execute(handle);
}
newCachedThreadPool.shutdown();
System.out.println("Main Thread: Finished at:" + new Date());
}
//固定长度的线程池
private static void fixedTest() {
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
Handle handle = new Handle(String.valueOf(i));
newFixedThreadPool.execute(handle);//先执行五个,然后再执行五个
}
newFixedThreadPool.shutdown();
System.out.println("Main Thread: Finished at:" + new Date());
}
//单线程线程池
private static void singleTest() {
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
Handle handle = new Handle(String.valueOf(i));
newSingleThreadExecutor.execute(handle);//单一线程分次执行
}
newSingleThreadExecutor.shutdown();
System.out.println("Main Thread: Finished at:" + new Date());
}
/**
* 固定长度的线程池,而且以延迟或者定时的方式来执行,类似Timer
*
* scheduleAtFixedRate 按指定频率周期执行某个任务
* scheduleAtFixedRate ,是以上一个任务开始的时间计时,period时间过去后,检测上一个任务是否执行完毕,
* 如果上一个任务执行完毕,则当前任务立即执行,如果上一个任务没有执行完毕,则需要等上一个任务执行完毕后立即执行。
*
* scheduleWithFixedDelay 周期定时执行某个任务/按指定频率间隔执行某个任务(注意)
* scheduleWithFixedDelay,是以上一个任务结束时开始计时,period时间过去后,立即执行。
*
*/
private static void scheduledTest() {
ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(10);
for (int i = 0; i < 3; i++) {
Handle handle = new Handle(String.valueOf(i));
//执行
//newScheduledThreadPool.execute(handle);
//延迟1秒执行
//newScheduledThreadPool.schedule(handle, 1, TimeUnit.SECONDS);
//延迟1秒,每隔3秒执行一遍(隔3秒执行,①如果上一次结束,立即执行;②如果上一次未结束,结束立即执行;)
//newScheduledThreadPool.scheduleAtFixedRate(handle, 1, 3, TimeUnit.SECONDS);
//延迟1秒,每隔3秒执行一遍(上一个任务结束开始计时)
newScheduledThreadPool.scheduleWithFixedDelay(handle, 1, 3, TimeUnit.SECONDS);
}
//newScheduledThreadPool.shutdown();
System.out.println("Main Thread: Finished at:" + new Date());
}
}
Calltable开启多线程
package com.guor.thread;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class TaskWithResult implements Callable<String> {
private int id;
public TaskWithResult(int id) {
this.id = id;
}
@Override
public String call() throws Exception {
System.out.println("call()方法被自动调用,干活!!! " + Thread.currentThread().getName());
Thread.sleep(2000);
return "call()方法被自动调用,任务的结果是:" + id + " " + Thread.currentThread().getName();
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
List<Future<String>> list = new ArrayList<Future<String>>();
// 创建10个任务并执行
for (int i = 0; i < 10; i++) {
//使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中
Future<String> future = newCachedThreadPool.submit(new TaskWithResult(i));
//将任务执行结果存储到List中
list.add(future);
}
//启动一次顺序关闭,执行以前提交的任务,但不接受新任务。如果已经关闭,则调用没有其他作用。
newCachedThreadPool.shutdown();
for(Future<String> future : list) {
System.out.println(future.get() + "--date--> " +new Date());
}
}
}