线程
线程分守护线程和用户线程,虚拟机不在乎守护线程,如果用户线程停止,守护线程也会停止
public class ThreadDemo {
public static void main(String[] args) {
God god = new God();
People people = new People();
Thread thread = new Thread(god);
thread.setDaemon(true);//设置守护线程
//理论上god线程应该不会停止,但是设置成了守护线程
//在people线程停止后,守护线程也会停止
thread.start();
new Thread(people).start();
}
}
class God implements Runnable{
@Override
public void run() {
while (true){
System.out.println("守护线程");
}
}
}
class People implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("用户线程:" + i);
}
}
}
线程创建的三种方式
- 继承Thread类
class PrimeThread extends Thread { long minPrime; PrimeThread(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } } PrimeThread p = new PrimeThread(143); p.start();
- 实现Runnable接口
class PrimeRun implements Runnable { long minPrime; PrimeRun(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } } PrimeRun p = new PrimeRun(143); //静态代理模式 new Thread(p).start();
- 实现Callable接口
public class CallableDemo implements Callable<Boolean> { private String name; public CallableDemo(String name) { this.name = name; } @Override public Boolean call() throws Exception { System.out.println("call...... " + name); return true; } public static void main(String[] args) throws ExecutionException, InterruptedException { CallableDemo zhangsan = new CallableDemo("zhangsan"); CallableDemo lisi = new CallableDemo("lisi"); CallableDemo wangwu = new CallableDemo("wangwu"); ExecutorService service = Executors.newFixedThreadPool(3); Future<Boolean> submit1 = service.submit(zhangsan); Future<Boolean> submit2 = service.submit(lisi); Future<Boolean> submit3 = service.submit(wangwu); Boolean aBoolean = submit1.get(); service.shutdownNow(); } }
调用start方法启动线程,由cpu决定何时运行
线程方法
方法 | 说明 |
---|---|
setPriority(int newPriority) | 更改线程优先级 |
static native void sleep(long millis) | 指定毫秒数内当前线程休眠 |
void join() | 等待该线程终止,主线程在for循环,A线程调用join,则主线程阻塞,等待A线程执行完毕。 |
static native void yield(); | 暂停当前正在执行的线程,并执行其他线程 |
public void interrupt() | 中断线程 |
同步方法和同步代码块
同步方法:
public class ThreadDemo {
public synchronized void method1(){
Thread.sleep(1000);
System.out.println("同步方法");
}
public synchronized void method2(){
System.out.println("同步方法");
}
}
同步代码块
synchronized(Object obj){
System.out.println("同步代码块");
}
synchronized 默认锁的是this。使用同步代码块可以指定锁定的对象。
也就是说
ThreadDemo demo = new ThreadDemo();
多个线程调用demo的method()方法,无论是method1还是method2,先拿到锁的先调用
public static void main(String[] args) {
ThreadDemo demo = new ThreadDemo();
new Thread(() -> {
demo.method1();
}, "A").start();
new Thread(() -> {
demo.method2();
}, "B").start();
// 无论如何都会先执行method1
}
如果是static方法,锁的是Class
没有的static,锁的是new ThreadDemo()这个具体的类
wait和sleep
- wait是object类的,sleep是Thread类的
- wait会释放锁,sleep不会释放锁
- wait必须在同步代码块中使用,sleep任何地方都可以使用
死锁
public class ThreadDemo {
public static void main(String[] args) {
Death zhangsan = new Death(0, "zhangsan");
Death lisi = new Death(1, "lisi");
//都拿自己的锁,去尝试获得对方的锁。结果造成死锁
zhangsan.start();
lisi.start();
}
}
class God {
}
class People {
}
class Death extends Thread {
static God god = new God();
static People people = new People();
int choice;
String name;
public Death(int choice, String name) {
this.choice = choice;
this.name = name;
}
@SneakyThrows
@Override
public void run() {
choice();
}
private void choice() throws InterruptedException {
if (choice == 0){
synchronized (god){
System.out.println("获得 god 的锁");
Thread.sleep(1000);
synchronized (people){
System.out.println("获得 people 的锁");
}
}
}else {
synchronized (people){
System.out.println("获得 people 的锁");
Thread.sleep(1000);
synchronized (god){
System.out.println("获得 god 的锁");
}
}
}
}
}
解决方法
private void choice() throws InterruptedException {
if (choice == 0) {
synchronized (god) {
System.out.println("获得 god 的锁");
Thread.sleep(1000);
}
synchronized (people) {
System.out.println("获得 people 的锁");
}
} else {
synchronized (people) {
System.out.println("获得 people 的锁");
Thread.sleep(1000);
}
synchronized (god) {
System.out.println("获得 god 的锁");
}
}
}
JUC
锁
synchronized
public class ThreadDemo {
public static void main(String[] args) {
Product product = new Product();
new Thread(()-> {
for (int i = 0; i < 60; i++) {
product.sell();
}
},"A").start();
new Thread(()-> {
for (int i = 0; i < 60; i++) {
product.sell();
}
},"B").start();
new Thread(()-> {
for (int i = 0; i < 60; i++) {
product.sell();
}
},"C").start();
}
}
class Product {
private Integer num = 50;
public synchronized void sell(){
if (num > 0){
System.out.println(Thread.currentThread().getName() + "卖出了:1张 ,还剩:" + (--num));
}
}
}
Lock
格式:
Lock l = ...; l.lock(); try { // access the resource protected by this lock } finally { l.unlock(); }
public class ThreadDemo {
public static void main(String[] args) {
Product product = new Product();
new Thread(() -> {
for (int i = 0; i < 60; i++) {
product.sell();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 60; i++) {
product.sell();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 60; i++) {
product.sell();
}
}, "C").start();
}
}
class Product {
private Integer num = 50;
private Lock lock = new ReentrantLock();
public void sell() {
lock.lock();
try {
if (num > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了:1张 ,还剩:" + (--num));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
区别:
- synchronized内置java关键字,Lock是一个java类
- synchronized无法判断获得锁状态,Lock可以判断是否获取了锁
- synchronized会自动释放锁,Lock必须手动释放锁
- synchronized其他线程尝试获得锁会等待,Lock比一定会等待下去
- synchronized可重入锁,不可中断,非公平锁;Lock,可重入锁,可以判断中断,默认非公平
- synchronized适合锁少量代码同步问题;Lock适合锁大量代码块
lock精确唤醒:Condition
/*
* A 执行完调用B,B执行完调用C,C执行完调用A
*/
public class C {
public static void main(String[] args) {
Data3 data = new Data3();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data.printA();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data.printB();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data.printC();
}
}, "C").start();
}
}
class Data3 {
// 资源类 Lock
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int number = 1;
// number=1 A执行 number=2 B执行 number=3 C执行
public void printA() {
lock.lock();
try {
// 业务,判断-> 执行-> 通知
while (number != 1) {
// A等待
condition1.await();
}
System.out.println(Thread.currentThread().getName() + "=>AAAAAAA");
// 唤醒,唤醒指定的人,B
number = 2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
// 业务,判断-> 执行-> 通知
while (number != 2) {
// B等待
condition2.await();
}
System.out.println(Thread.currentThread().getName() + "=>BBBBBBBBB");
// 唤醒,唤醒指定的人,c
number = 3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
// 业务,判断-> 执行-> 通知
// 业务,判断-> 执行-> 通知
while (number != 3) {
// C等待
condition3.await();
}
System.out.println(Thread.currentThread().getName() + "=>CCCCC ");
// 唤醒,唤醒指定的人,A
number = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
公平锁、非公平锁
公平锁: 不能够插队,必须先来后到
非公平锁:可以插队
ReentrantLock
可重入锁(递归锁)
拿到外边的锁,就可以拿到里边的锁
public class Demo01 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sms();
},"A").start();
new Thread(()->{
phone.sms();
},"B").start();
}
}
class Phone{
public synchronized void sms(){
System.out.println(Thread.currentThread().getName()
+ "sms");
call(); // 这里也有锁(sms锁 里面的call锁)
}
public synchronized void call(){
System.out.println(Thread.currentThread().getName()
+ "call");
}
}
自旋锁
Callable
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// new Thread(new Runnable()).start();// 启动Runnable
// new Thread(new FutureTask<V>()).start();
// new Thread(new FutureTask<V>( Callable )).start();
//FutureTask是Runnable的实现类,可以传入Callable
// new 一个MyThread实例
MyThread thread = new MyThread();
// MyThread实例放入FutureTask
FutureTask futureTask = new FutureTask(thread); // 适配类
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start(); // call()方法结果会被缓存,提高效率,因此只打印1个call
// 这个get 方法可能会产生阻塞!把他放到最后
Integer o = (Integer) futureTask.get();
// 或者使用异步通信来处理!
System.out.println(o);// 1024
}
}
class MyThread implements Callable<Integer> {
@Override
public Integer call() {
System.out.println("call()"); // A,B两个线程会打印几个call?(1个)
// 耗时的操作
return 1024;
}
}
常用的辅助类
CountDownLatch
减法计数器: 实现调用几次线程后 再触发某一个任务
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
// 总数是6,必须要执行任务的时候,再使用
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <=6 ; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()
+" 执行");
countDownLatch.countDown(); // 数量-1
},String.valueOf(i)).start();
}
countDownLatch.await(); // 等待计数器归零,然后再向下执行
System.out.println("结束");
}
}
CyclicBarrier
加法计数器:实现调用几次线程后 再触发某一个任务
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(10, () -> {
System.out.println("线程执行10次后执行!");
});
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " 执行");
try {
cyclicBarrier.await(); // 等待
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
Semaphore
计数信号量
public class SemaphoreDemo {
public static void main(String[] args) {
// 线程数量:停车位! 限流!、
// 如果已有3个线程执行(3个车位已满),则其他线程需要等待‘车位’释放后,才能执行!
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <=6 ; i++) {
new Thread(()->{
try {
semaphore.acquire();//获得,假设如果已经满了,等待,等待被释放为止
System.out.println(Thread.currentThread().getName()+"抢到车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(); // release() 释放 ,会将当前的信号量释放 + 1
}
},String.valueOf(i)).start();
}
}
}
读写锁 ReadWriteLock
/**
* 独占锁(写锁) 一次只能被一个线程占有
* 共享锁(读锁) 多个线程可以同时占有
* ReadWriteLock
* 读-读 可以共存!
* 读-写 不能共存!
* 写-写 不能共存!
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCacheLock myCacheLock = new MyCacheLock();
// 写入
for (int i = 1; i <= 5 ; i++) {
final int temp = i;
new Thread(()->{
myCacheLock.put(temp+"",temp+"");
},String.valueOf(i)).start();
}
// 读取
for (int i = 1; i <= 5 ; i++) {
final int temp = i;
new Thread(()->{
myCacheLock.get(temp+"");
},String.valueOf(i)).start();
}
}
}
/**
* 自定义缓存
* 加锁的
*/
class MyCacheLock{
private volatile Map<String,Object> map = new HashMap<>();
// 读写锁: 更加细粒度的控制
private ReadWriteLock readWriteLock = new
ReentrantReadWriteLock();
// private Lock lock = new ReentrantLock();
// 存,写入的时候,只希望同时只有一个线程写
public void put(String key,Object value){
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()
+"写入"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()
+"写入OK");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
}
// 取,读,所有人都可以读!
public void get(String key){
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()
+"读取"+key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName()
+"读取OK");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
阻塞队列BlockingQueue
方式 | 抛出异常 | 有返回值,不抛出异常 | 阻塞 等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer() | put() | offer(,) |
移除 | remove | poll() | take() | poll(,) |
检测队首元素 | element | peek() | - | - |
public class BlockingDeque {
public static void main(String[] args) {
// 队列的大小
LinkedBlockingQueue<String> blockingQueue = new LinkedBlockingQueue<String>();
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName()
+ " put 1");
// put进入一个元素
blockingQueue.put("1");
System.out.println(Thread.currentThread().getName()
+ " put 2");
blockingQueue.put("2");
System.out.println(Thread.currentThread().getName()
+ " put 3");
blockingQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "T1").start();
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName()
+ "=>" + blockingQueue.take());
System.out.println(Thread.currentThread().getName()
+ "=>" + blockingQueue.take());
System.out.println(Thread.currentThread().getName()
+ "=>" + blockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "T2").start();
}
}
SynchronousQueue 同步队列
没有容量,进去一个元素,必须等待取出来之后,才能再往里面放一个元素
public class SynchronousQueueDemo {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue =
new SynchronousQueue<>(); // 同步队列
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()
+" put 1");
// put进入一个元素
blockingQueue.put("1");
System.out.println(Thread.currentThread().getName()
+" put 2");
blockingQueue.put("2");
System.out.println(Thread.currentThread().getName()
+" put 3");
blockingQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"T1").start();
new Thread(()->{
try {
// 睡眠3s取出一个元素
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()
+"=>"+blockingQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()
+"=>"+blockingQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()
+"=>"+blockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
},"T2").start();
}
}
线程池
线程池的好处:
- 降低系统资源的消耗
- 提高响应的速度
- 方便管理
7大参数
public ThreadPoolExecutor(int corePoolSize, // 核心线程池大小
int maximumPoolSize, // 最大核心线程池大小
long keepAliveTime, // 超时没有人调用就会释放
TimeUnit unit, // 超时单位
// 阻塞队列
BlockingQueue<Runnable> workQueue,
// 线程工厂:创建线程的,一般 不用动
ThreadFactory threadFactory,
// 拒绝策略
RejectedExecutionHandler handle )
线程池:4种拒绝策略
- new ThreadPoolExecutor.AbortPolicy()
队列满了,不处理,抛出异常 - new ThreadPoolExecutor.CallerRunsPolicy()
哪来的去哪里!队列满了,拒绝任务,会在提交的主线程执行,可能会阻塞主线程。 - new ThreadPoolExecutor.DiscardPolicy()
队列满了,丢掉任务,不会抛出异常! - new ThreadPoolExecutor.DiscardOldestPolicy()
队列满了,尝试去和最早的竞争,也不会抛出异常!
定义最大线程数
CPU密集型:几核就设置为几,可以保持CPU效率最高
IO密集型:判断任务中十分耗费IO的线程数,大于这个数,可以设置为2倍
四大函数式编程
lambda表达式、链式编程、函数式接口、Stream流式计算
函数式接口
只有一个方法的接口。函数式接口都可以用lambda表达式简化
Function 函数式接口:
//Function<T, R> 传入参数T,返回参数R
Function<String,Integer> function = new Function<String, Integer>() {
@Override
public Integer apply(String s) {
//具体实现
return null;
}
};
//简化
Function<String,String> function = str->{return str;};
System.out.println(function.apply("aaaa"));
Predicate 断定型接口:
// 传入一个T类型,返回一个boolean
Predicate<String> predicate =(str) -> {return str.isEmpty();};
System.out.println(predicate.test(""));//true
Consumer 消费型接口:
//只接收参数,处理后不返回
Consumer<String> consumer = (str)->{System.out.println(str);};
consumer.accept("sdadasd");
Supplier 供给型接口:
// 不传入参数,有返回
Supplier<Integer> supplier = ()->{return 1024; };
System.out.println(supplier.get());
Stream 流式计算
Stream 的很多方法都是传入函数式接口
过滤
/**
* 题目要求:一分钟内完成此题,只能用一行代码实现!
* 现在有5个用户!筛选:
* 1、ID 必须是偶数
* 2、年龄必须大于23岁
* 3、用户名转为大写字母
* 4、用户名字母倒着排序
* 5、只输出一个用户!
*/
public class Test {
public static void main(String[] args) {
User u1 = new User(1, "a", 21);
User u2 = new User(2, "b", 22);
User u3 = new User(3, "c", 23);
User u4 = new User(4, "d", 24);
User u5 = new User(6, "e", 25);
// 集合就是存储
List<User> list = Arrays.asList(u1, u2, u3, u4, u5);
// 计算交给Stream流
// lambda表达式、链式编程、函数式接口、Stream流式计算
list.stream()
.filter(u -> {
return u.getId() % 2 == 0;
})// ID 必须是偶数
.filter(u -> {
return u.getAge() > 23;
})// 年龄必须大于23岁
// 用户名转为大写字母
.map(u -> {
return u.getName().toUpperCase();
})
// 用户名字母倒着排序
.sorted((uu1, uu2) -> {
return uu2.compareTo(uu1);
})
.limit(1)// 只输出一个用户!
.forEach(System.out::println);
}
}
计算
List<Integer> list = Arrays.asList(1,44,165,4,654,5,484,446);
IntSummaryStatistics summaryStatistics = list.stream().mapToInt((s) -> s).summaryStatistics();
System.out.println("总和:" + summaryStatistics.getSum());
System.out.println("平均数:" + summaryStatistics.getAverage());
System.out.println("总个数:" + summaryStatistics.getCount());
System.out.println("最大值:" + summaryStatistics.getMax());
System.out.println("最小值:" + summaryStatistics.getMin());
User u1 = new User(1, "a", 21);
User u2 = new User(2, "b", 22);
User u3 = new User(3, "c", 23);
User u4 = new User(4, "d", 24);
User u5 = new User(6, "e", 25);
List<User> list = Arrays.asList(u1, u2, u3, u4, u5);
int sum = userList.stream().mapToInt(User::getAge).sum();
ForkJoin:并行执行任务
特点:工作窃取,里面维护的都是双端队列
**
* 计算1加到1000000000
*/
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
test1(); // 6981
test2(); // 4900
test3(); // 196
}
//普通for循环
public static void test1(){
Long sum = 0L;
long start = System.currentTimeMillis();
for (Long i = 1L; i <= 10_0000_0000L; i++) {
sum += i;
}
long end = System.currentTimeMillis();
System.out.println("sum="+sum+" 时间:"+(end-start));
}
// 使用ForkJoin
public static void test2() throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinDemo(
0L, 10_0000_0000L);
// 提交任务
ForkJoinTask<Long> submit = forkJoinPool.submit(task);
Long sum = submit.get();// 获得结果
long end = System.currentTimeMillis();
System.out.println("sum="+sum+" 时间:"+(end-start));
}
//使用Stream并行流
public static void test3(){
long start = System.currentTimeMillis();
// Stream并行流 () (]
long sum = LongStream
.rangeClosed(0L, 10_0000_0000L) // 计算范围(,]
.parallel() // 并行计算
.reduce(0, Long::sum); // 输出结果
long end = System.currentTimeMillis();
System.out.println("sum="+sum+" 时间:"+(end-start));
}
}
class ForkJoinDemo extends RecursiveTask<Long> {
private Long start; // 1
private Long end; // 1990900000
// 临界值
private Long temp = 500000L;
public ForkJoinDemo(Long start, Long end) {
this.start = start;
this.end = end;
}
// 计算方法
@Override
protected Long compute() {
if ((end-start)<temp){
Long sum = 0L;
for (Long i = start; i <= end; i++) {
sum += i;
}
return sum;
}else {
// forkjoin 递归
long middle = (start + end) / 2; // 中间值
ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
task1.fork(); // 拆分任务,把任务压入线程队列
ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end);
task2.fork(); // 拆分任务,把任务压入线程队列
return task1.join() + task2.join();
}
}
}
异步回调Future
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 没有返回值的 runAsync 异步回调
CompletableFuture<Void> completableFuture1 =
CompletableFuture.runAsync(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+";异步回调,无返回值");
});
System.out.println("1111");
completableFuture1.get(); // 获取阻塞执行结果
// 有返回值的 supplyAsync 异步回调
// ajax,成功和失败的回调
// 返回的是错误信息;
CompletableFuture<Integer> completableFuture2 =
CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + " ;异步回调,有返回值");
int i = 10 / 0;
return 1024;
});
System.out.println(completableFuture2.whenComplete((t, u) -> {
System.out.println("t=>" + t); // 正常的返回结果
System.out.println("u=>" + u); // 错误信息
}).exceptionally((e) -> {
System.out.println(e.getMessage());
return 233; // 可以获取到错误的返回结果
}).get());
}
JMM
Java内存模型(java memory model)本身是一种抽象的概念,并不真实存在
内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的
- lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
- unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
- read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
- load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
- use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
- assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
- store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
- write (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中
JMM 对这八种指令的使用,制定了如下规则:
- 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write
- 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存
- 不允许一个线程将没有assign的数据从工作内存同步回主内存
- 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assign和load操作
- 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁
- 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值
- 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量
- 对一个变量进行unlock操作之前,必须把此变量同步回主内存
Volatile
Volatile 是 Java 虚拟机提供轻量级的同步机制,类似于synchronized 但是没有其强大。
- 保证可见性:让线程知晓主内存的变化
- 不保证原子性:
- 防止指令重排
单例模式
单例模式都要将构造器私有化
饿汉式
// 饿汉式单例
public class Hungry {
// 可能会浪费空间
private byte[] data = new byte[1024*1024];
private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
DCL(双重检测锁模式) 懒汉式
public class LazyMan {
private static boolean csp = false;// 标志位
// 单例不安全,因为反射可以破坏单例,如下解决这个问题:
private LazyMan(){
synchronized (LazyMan.class){
if (csp == false){
csp = true;
}else {
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
}
// 原子性操作:避免指令重排
private volatile static LazyMan lazyMan;
// 双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if (lazyMan==null){
synchronized (LazyMan.class){
if (lazyMan==null){
lazyMan = new LazyMan(); // 不是一个原子性操作
}
}
}
return lazyMan;
}
}
单例不安全,因为反射可以破坏单例
枚举
public enum Direction {
FRONT() {
public void fun() {
System.out.println("FROND:重写了fun()方法");
}
},
BEHIND() {
public void fun() {
System.out.println("BEHIND:重写了fun()方法");
}
},
LEFT() {
public void fun() {
System.out.println("LEFT:重写了fun()方法");
}
},
RIGHT() {
public void fun() {
System.out.println("RIGHT:重写了fun()方法");
}
};
public abstract void fun()[只需要把fun()方法修改为抽象方法,但不可以把Direction类声明为抽象类。];
}
CAS:比较并交换
AtomicInteger自增方法
atomicInteger.getAndIncrement();
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
//比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环!
return var5;
}
ABA问题:
现在有数据a = 1
A线程修改a 修改为2,
B线程要修改a 为3,
但是A线程执行的比较快,先将a 修改为2,之后又将a 修改为1
B线程仍能修改成功
解决ABA 问题,引入原子引用! 对应的思想:乐观锁!
public class CASDemo {
/*
* AtomicStampedReference 原子引用,
* 如果泛型是一个包装类,注意对象的引用问题
* 正常在业务操作,这里面比较的都是一个个对象
*/
// 可以有一个初始对应的版本号 1
static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);
// CAS compareAndSet : 比较并交换!
public static void main(String[] args) {
new Thread(() -> {
// 获得版本号
int stamp = atomicStampedReference.getStamp();
System.out.println("a1=>" + stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(1, 2,
atomicStampedReference.getStamp(), // 最新版本号
// 更新版本号
atomicStampedReference.getStamp() + 1);
System.out.println("a2=>" + atomicStampedReference.getStamp());
System.out.println(
atomicStampedReference.compareAndSet(
2,
1,
atomicStampedReference.getStamp(),
atomicStampedReference.getStamp() + 1) + " a");
System.out.println("a3=>" + atomicStampedReference.getStamp());
}, "a").start();
// 乐观锁的原理相同!
new Thread(() -> {
// 获得版本号
int stamp = atomicStampedReference.getStamp();
System.out.println("b1=>" + stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(
atomicStampedReference.compareAndSet(
1, 5, stamp, stamp + 1) + " b");
System.out.println("b2=>"
+ atomicStampedReference.getStamp());
}, "b").start();
}
}