package com.example.demo1.demo02;
/**
* @author liar
*/
public class TestThread {
public static void main(String[] args) {
Eat eat = new Eat();
Out out = new Out();
eat.start();
out.start();
}
}
class Eat extends Thread{
@Override
public void run() {
for (int i = 0; i < 500; i++) {
System.out.println( "我在吃饭呢");
}
}
}
class Out extends Thread{
@Override
public void run() {
for (int i = 0; i < 500; i++) {
System.out.println("我在拉屎呢");
}
}
}
其核心是重写run方法;其运行结果是:
实现Runnable接口
package com.example.demo1.demo02;
/**
* @author liar
*/
public class TestRunnable {
public static void main(String[] args) {
Who who = new Who();
FBI fbi = new FBI();
Thread who1 = new Thread(who,"who");
Thread fbi1 = new Thread(fbi,"FBI");
who1.start();
fbi1.start();
}
}
class Who implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("Who are you ?");
}
}
}
class FBI implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("Open the door!!!FBI!!!");
}
}
}
运行结果也如预期。
实现Callable接口
package com.example.demo1.demo02;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class TestCallable {
public static void main(String[] args) {
Hit hit = new Hit();
Connection connection = new Connection();
FutureTask futureTask1 = new FutureTask(hit);
FutureTask futureTask2 = new FutureTask(connection);
Thread t1 = new Thread(futureTask1);
Thread t2 = new Thread(futureTask2);
t1.start();
t2.start();
try {
//get()方法的返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值
Object o1 = futureTask1.get();
Object o2 = futureTask2.get();
System.out.println(o1);
System.out.println(o2);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class Hit implements Callable{
@Override
public Object call() throws Exception {
for (int i = 0; i < 500; i++) {
System.out.println("怎么样!!你打我啊!!!");
}
return "hit";
}
}
class Connection implements Callable{
@Override
public Object call() throws Exception {
for (int i = 0; i < 500; i++) {
System.out.println("小伙子,出来混,是要讲人脉的!!!");
}
return "connection";
}
}
其运行结果:
其与前两者的区别是,可以通过FutureTask获取返回值的。
2.2、总结
普通的线程代码:继承Thread类
Runnable接口:没有返回值,效率相比于Callable接口较低
2.3、线程和进程
关于线程和进程,如果不能用一句话说出来,说明你掌握的还不够扎实!
进程:
一个正在执行的程序
例如:QQ.exe;是运行程序的集合
一个进程可以包含多个线程且至少包含一个线程
Java真的能开启线程吗?
不能开启线程
分析Thread类的原码了解,最后该类还是调用了本地方法(native)
Java的底层是c++
2.4、并发和并行
并发:(多线程操作同一资源)
CPU一核,模拟出来多线程;天下武功唯快不破,快速交替
并行:(多个人同时走路)
CPU多核,多个线程同时执行
并发编程的本质:充分利用CPU的资源
2.5、线程有几个状态
public enum State {
NEW,//新生
RUNNABLE,//运行
BLOCKED,//阻塞
WAITING,//等待,一直等待
TIMED_WAITING,//超时等待,过期不候
TERMINATED;//终止
}
2.6、wait和sleep的区别
来自不同的类:
wait==>Object
sleep==>Thread
关于锁的释放
wait:会释放锁
sleep:“抱着锁睡觉,不会释放锁”
使用的范围是不同的
wait:必须在同步代码块中使用
sleep:任何地方
是否需要捕获异常
wait:不需要捕获异常(什么叫中断异常?)
sleep:需要捕获异常(存在超时等待的情况)
三、Lock锁(重点)
3.1、回忆"synchronized"
作用:
保证线程安全,例如当多个线程访问统一资源时,会发生数据紊乱问题;
怎么理解:
队列+锁
实例:没有synchronized修饰的方法
package com.example.demo1.demo02;
/**
* @author liar
*/
public class UnsafeTicket {
public static void main(String[] args) {
BuyTicket ticket = new BuyTicket();
new Thread(ticket,"倒霉的我").start();
new Thread(ticket,"幸运的你们").start();
new Thread(ticket,"可恶的黄牛党").start();
}
}
class BuyTicket implements Runnable{
private int tickets = 10;
private boolean flag = true;
@Override
public void run() {
while (flag){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
buy();
}
}
//在方法名前加上"synchronized",那么该方法就变成了同步方法了
//!!!锁的是"this"!!!
//'synchronized'默认锁的是this
private synchronized void buy(){
if (tickets<=0){
flag=false;
//这个return放在这里,如果符合if条件,那么返回出去,不执行下面的sout语句
return;
}
System.out.println(Thread.currentThread().getName()+"买到了第"+tickets--+"张票");
}
}
我们看一下运行效果:
实例:有synchronized修饰的方法
package com.example.demo1.demo02;
/**
* @author liar
*/
public class UnsafeTicket {
public static void main(String[] args) {
BuyTicket ticket = new BuyTicket();
new Thread(ticket,"倒霉的我").start();
new Thread(ticket,"幸运的你们").start();
new Thread(ticket,"可恶的黄牛党").start();
}
}
class BuyTicket implements Runnable{
private int tickets = 10;
private boolean flag = true;
@Override
public void run() {
while (flag){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
buy();
}
}
//在方法名前加上"synchronized",那么该方法就变成了同步方法了
//!!!锁的是"this"!!!
//'synchronized'默认锁的是this
private synchronized void buy(){
if (tickets<=0){
flag=false;
//这个return放在这里,如果符合if条件,那么返回出去,不执行下面的sout语句
return;
}
System.out.println(Thread.currentThread().getName()+"买到了第"+tickets--+"张票");
}
}
这下,就是线程安全的了。
3.2、Lock接口(Lock锁)
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//基本的卖票例子
/*
真正的多线程开发,公司中的开发,降低耦合性
线程就是一个单独的资源类,没有任何附属操作
1、属性、方法
*/
public class SaleTicketDemo2 {
public static void main(String[] args) {
//并发:多线程操作同一个类,把资源类丢入线程
Ticket2 ticket = new Ticket2();
//@FunctionalInterface 函数式接口,jdk1.8 lambda表达式 (参数)->{代码}
new Thread( () ->{
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"A").start();
new Thread( () ->{
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"B").start();
new Thread( () ->{
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"C").start();
}
}
//Lock三部曲
//1.new ReentrantLock();
//2. lock.lock(); 加锁
//3.finally --> lock.unlock(); 解锁
class Ticket2{
//属性方法
private int number = 50;
Lock lock = new ReentrantLock();
public void sale(){
lock.lock(); //加锁
try{
//业务代码
if(number > 0){
System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余" + number);
}
}catch (Exception e){
e.printStackTrace();
}finally {
//解锁
lock.unlock();
}
}
}
package com.my.lock8;
import java.util.concurrent.TimeUnit;
/*
* 8锁现象:其实就是8个关于锁的问题
* 7、将两个方法一个设置为静态方法,一个设置为普通同步方法,创建一个资源类对象,执行两个方法,先打印“发短信”还是“打电话”(打电话 发短信)
* 8、创建两个资源类对象,调用下面的两个方法,先打印“发短信”还是“打电话”(打电话 发短信)
* */
public class Test04 {
public static void main(String[] args) {
Phone04 phone1 = new Phone04();
Phone04 phone2 = new Phone04();
new Thread(()->{
phone1.sendMessage();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
class Phone04{
//静态的同步方法:锁的是Class模板
public static synchronized void sendMessage(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
//普通的同步方法:锁的是方法调用者
public synchronized void call(){
System.out.println("打电话");
}
}
5.2、总结
明白synchronized修饰的普通同步方法和加上static关键字的静态同步方法的区别
普通方法和同步方法的区别
六、集合类不安全
6.1、List结合不安全
package com.my.list;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
//集合是不安全的
//java.util.ConcurrentModificationException:并发修改异常
public class ListTest {
public static void main(String[] args) {
/*
* 并发线程下,ArrayList集合不安全,怎么解决?
* 1、使用List接口的古老实现类:Vector--List list = new Vector<>();
* 2、使用Collections工具类:List
6.2、Set集合不安全
package com.my.list;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
public class SetTest {
public static void main(String[] args) {
/*
* 和ArrayList同样,线程不安全,解决方法?
* 1、Set set = Collections.synchronizedSet(new HashSet<>());
* 2、Set set = new CopyOnWriteArraySet<>();
* */
// Set set = new HashSet<>();
Set set = new CopyOnWriteArraySet<>();
for (int i = 0; i < 50; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
6.3、Map集合不安全
package com.my.list;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class MapTest {
public static void main(String[] args) {
/*
* 工作中是这么用HashMap?--不是这么用的,工作中不要HashMap
* 它默认等价于什么?--new HashMap<>(16,0.75);
* */
//搞清楚什么是加载因子?什么是初始化容量?
/*
* map线程不安全怎么解决?
* 1、Map map = Collections.synchronizedMap(new HashMap<>());
* 2、Map map = new ConcurrentHashMap<>();
* 3、研究ConcurrentHashMap原理
* */
// Map map = new HashMap<>();
Map map = new ConcurrentHashMap<>();
for (int i = 0; i < 20; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
}).start();
}
}
}
七、走进Callable
Callable:
函数式接口(可以使用lombda表达式)
有返回值
可以抛出异常
方法不同,这里的是call方法
此图引用爱睡觉哦。
package com.my.callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread myThread = new MyThread();
//怎么使用Callable?
/*
* 原来Runnable接口的实现方式:new Thread(new MyThread()).start();
*
* */
FutureTask futureTask = new FutureTask(myThread);//适配类,作为中间“驿站”,为了能够使用Callable接口的实现类
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start();//结果会被缓存!--call方法只执行一次!
//获取Callable方法中的返回值
Object str = futureTask.get();//这个get方法可能会产生阻塞,一般放在最后!或者用过异步通信来处理
System.out.println(str);
}
}
class MyThread implements Callable {
@Override
public String call() throws Exception {
System.out.println("执行了call方法!");
return "哈哈哈哈";
}
}
八、常用的辅助类
8.1、CountDownLatch(“减法计数器”)
常用方法:
countDown()
await()
package com.my.assist;
import java.util.concurrent.CountDownLatch;
//计算器
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i <= 6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"go out!");
countDownLatch.countDown();//就是“-1”操作
},String.valueOf(i)).start();
}
countDownLatch.await();//阻塞,判断countDownLatch是否归零,归零后再往下执行
System.out.println("Closed door!");
}
}
8.2、CyclicBarrier(“加法计数器”)
package com.my.assist;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
//加法计数器
public class CyclicBarrierTest {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("召唤神龙成功!");
});
for (int i = 1; i <= 7; i++) {
final int temp = i;
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"收集了"+temp+"颗,龙珠!");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
8.3、Semaphore(“信号量”)
常用方法:
semaphore.acquire():获得
semaphore.release():释放
作用:多个共享资源互斥使用!并发限流,控制最大的线程数目!
package com.my.assist;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
//信号量
public class SemaphoreTest {
public static void main(String[] args) {
//以抢车位为例
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();
}
}).start();
}
}
}
九、读写锁(ReentrantReadWriteLock)
ReentrantReadWriteLock它是ReadWriteLock接口的实现类:
读-读:可以共享
读-写:不可以共享
写-写:不可以共享
package com.example.demo1.demo02;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @author liar
*/
public class TestReadWriteLock {
public static void main(String[] args) {
MyCacheLock myCache = new MyCacheLock();
//写线程
for (int i = 1; i <= 5; i++) {
final int temp = i;
new Thread(()->{
myCache.put(temp+"",temp+"");
},String.valueOf(i)).start();
}
for (int i = 1; i <= 5; i++) {
final int temp = i;
new Thread(()->{
myCache.get(temp+"");
},String.valueOf(i)).start();
}
}
}
class MyCacheLock{
//volatile:关键字
public volatile Map map = new HashMap();
/*
* 为了提高效率,只允许有一个线程来写,但允许多个线程来读
* */
//读写锁:(并不是普通的Lock锁),是一种更加“细粒度”的锁
private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
//存--》写的过程
public void put(String key,Object value){
reentrantReadWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"写入"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
reentrantReadWriteLock.writeLock().unlock();
}
}
//去--》读的过程
public void get(String key){
reentrantReadWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"读入"+key);
map.get(key);
System.out.println(Thread.currentThread().getName()+"读入成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
reentrantReadWriteLock.readLock().unlock();
}
}
}
//自定义缓存
class MyCache{
//volatile:关键字
public volatile Map map = new HashMap();
/*
* 为了提高效率,只允许有一个线程来写,但允许多个线程来读
* */
//存--》写的过程
public void put(String key,Object value){
System.out.println(Thread.currentThread().getName()+"写入"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入成功");
}
//去--》读的过程
public void get(String key){
System.out.println(Thread.currentThread().getName()+"读入"+key);
map.get(key);
System.out.println(Thread.currentThread().getName()+"读入成功");
}
}
package com.my.function;
import java.util.function.Function;
//Function:函数型接口(接口中只有一个方法)
//接口实现类-》匿名内部类-》Lambda表达式
public class FunctionTest {
public static void main(String[] args) {
//匿名内部类
Function function = new Function() {
//该方法的作用:输出输入的值
@Override
public String apply(String str) {
return str;
}
};
//Lambda表达式
Function function1 = (str)->{return str;};
System.out.println(function.apply("麻腾飞"));
System.out.println(function1.apply("Matengfei"));
}
}
2、Predicate(断定型接口)
package com.my.function;
import java.util.function.Predicate;
/*
* 断定型接口
* */
public class PredicateTest {
public static void main(String[] args) {
//判断字符串是否为空
Predicate predicate = new Predicate() {
@Override
public boolean test(String str) {
return str.isEmpty();
}
};
Predicate predicate1 = (str)->{return str.isEmpty();};
System.out.println(predicate.test(""));
System.out.println(predicate1.test(""));
}
}
3、Consumer(消费型接口)
package com.my.function;
import java.util.function.Consumer;
/*
* 消费型接口:只有输入
* */
public class ConsumerTest {
public static void main(String[] args) {
//只接收输入的参数
Consumer consumer = new Consumer() {
@Override
public void accept(String str) {
System.out.println(str);
}
};
Consumer consumer1 = (str)->{
System.out.println(str);
};
consumer.accept("Matengfei");
consumer1.accept("Matengfei");
}
}
4、Supplier(供给型接口)
package com.my.function;
import java.util.function.Supplier;
/*
* 供给型接口:只输出,不输入
* */
public class SupplierTest {
public static void main(String[] args) {
Supplier supplier = new Supplier() {
@Override
public String get() {
System.out.println("get");
return "1024";
}
};
Supplier supplier1 = ()->{return "110";};
System.out.println(supplier.get());
System.out.println(supplier1.get());
}
}
十三、Stream流式计算
13.1、什么是Stream流式计算
存储+计算
存储:集合、MySQL、分布式…
计算:交给“流”来做
13.2.具体示例
package com.my.stream;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
public class StreamTest {
public static void main(String[] args) {
/*
* 现在有五个用户,按条件进行筛选:
* 1、ID必须是偶数
* 2、年龄必须大于20岁
* 3、用户名转为大写字母
* 4、用户名字母倒着排序
* 5、只输出一个用户
* */
User user1 = new User(1, "a", 18);
User user2 = new User(2, "b", 12);
User user3 = new User(4, "d", 22);
User user4 = new User(5, "f", 30);
User user5 = new User(6, "z", 40);
//存储
List list = Arrays.asList(user1, user2, user3, user4, user5);
//计算
//搞明白:System.out::println
//Lambda表达式、链式编程、函数式接口、Stream流式计算
list.stream().filter((u)->{return u.getId()%2==0;})
.filter((u)->{return u.getAge()>20;})
.map((u)->{return u.getName().toUpperCase();})
.sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
.limit(1)
.forEach(System.out::println);
}
}
十四、ForkJoin
14.1、什么是ForkJoin(分支合并)
ForkJoin在JDk1.7之后出现,并行执行任务!提高效率,数据量大!
14.2、ForkJoin特点
工作窃取:提高工作效率!(“双端队列”)
14.3、ForkJoin实例
package com.my.forkjoin;
import java.util.concurrent.RecursiveTask;
public class ForkJoinDemo extends RecursiveTask {
private Long start;
private Long end;
//临界值
private Long temp=10000L;
public ForkJoinDemo(Long start, Long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if ((end-start)
package com.my.forkjoin;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//test01();//13999
//test02();//10833
test03();//1147
}
public static void test01(){
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,可以通过临界值(temp)来进行调优
public static void test02() throws ExecutionException, InterruptedException {
Long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinDemo task = new ForkJoinDemo(0L, 10_0000_0000L);
ForkJoinTask submit = forkJoinPool.submit(task);
Long sum = submit.get();
Long end = System.currentTimeMillis();
System.out.println("sum="+sum+";时间:"+(end-start));
}
//使用Stream流式计算
public static void test03(){
Long start = System.currentTimeMillis();
long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
Long end = System.currentTimeMillis();
System.out.println("sum="+sum+";时间:"+(end-start));
}
}
package com.my.volatileTest;
/*
* 模拟8种操作存在的问题
* */
public class VolatileTest {
private static int num = 0;
public static void main(String[] args) {//main线程
new Thread(()->{//线程A
while (num==0){
}
}).start();
num = 1;
System.out.println("num=>"+num);
}
}
17.1、保证可见性
package com.my.volatileTest;
/*
* 模拟8种操作存在的问题
* */
public class VolatileTest {
//加上volatile关键字:保证可见性
private volatile static int num = 0;
public static void main(String[] args) {//main线程
new Thread(()->{
while (num==0){
}
}).start();
num = 1;
System.out.println("num=>"+num);
}
}
17.2、不保证原子性
原子性:不可分割
线程在执行时,不能被干扰,不可分割;要么成功,要么失败
package com.my.volatileTest;
//证明:Volatile关键字不保证原子性
public class VolatileTest02 {
private volatile static int num = 0;
public static void add(){
num++;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int i1 = 0; i1 < 1000; i1++) {
add();
}
}).start();
}
while (Thread.activeCount()>2){//main线程、gc线程
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+"num=>"+num);
}
}
问:如果不使用lock和synchronized,怎么保证原子性?
package com.my.volatileTest;
import java.util.concurrent.atomic.AtomicInteger;
//证明:Volatile关键字不保证原子性
public class VolatileTest02 {
//private volatile static int num = 0;
//原子类的Integer
private static AtomicInteger num = new AtomicInteger();
public static void add(){
//num++;
num.getAndIncrement();//AtomicInteger中的+1方法,要想更详细了解:CAS
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int i1 = 0; i1 < 1000; i1++) {
add();
}
}).start();
}
while (Thread.activeCount()>2){//main线程、gc线程
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+"num=>"+num);
}
}
注:这些原子类的底层和操作系统有关!在内存中修改值!Unsafe类是一个很特殊的存在
17.3、指令重排
什么是指令重排?
我们写的程序,实际在计算机中并不是按照我们想象的顺序执行的
源代码——》编译器优化的重排——》指令并行也可能重排——》内存系统也可能会重排——》执行
int x=1;//a
int y=2;//b
x=x+y;//c
y=x*x;//d
我们期望的执行顺序:abcd;实际上可能是bacd
那可不可能是:cdab
package com.my.cas;
import java.util.concurrent.atomic.AtomicInteger;
public class CASDemo {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2022);
// public final boolean compareAndSet(int expect, int update)
//期望,更新
atomicInteger.compareAndSet(2022,2023);
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2022, 2000));
System.out.println(atomicInteger.get());
}
}
Unsafe类
19.1、ABA问题
package com.my.cas;
import java.util.concurrent.atomic.AtomicInteger;
public class CASDemo {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2022);
// public final boolean compareAndSet(int expect, int update)
//期望,更新
//捣乱的线程B
atomicInteger.compareAndSet(2022,2023);
System.out.println(atomicInteger.get());
atomicInteger.compareAndSet(2023,2022);
System.out.println(atomicInteger.get());
//期望的线程A
System.out.println(atomicInteger.compareAndSet(2022, 2000));
System.out.println(atomicInteger.get());
}
}
二十、原子引用
模拟ABA场景:
package com.my.cas;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;
public class ABATest {
public static void main(String[] args) {
//Integer:反是这种包装类(引用数据类型)看看有没有超出返回,如果超出范围,会new一个新的对象
AtomicStampedReference stampedReference = new AtomicStampedReference<>(1,1);
new Thread(()->{
int stamp = stampedReference.getStamp();
System.out.println("a1=>"+stamp);
//a、b线程在这里一起休眠两秒
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
stampedReference.compareAndSet(1, 8, stampedReference.getStamp(), stampedReference.getStamp()+1);
System.out.println("a2=>"+stampedReference.getStamp());
stampedReference.compareAndSet(8, 1, stampedReference.getStamp(), stampedReference.getStamp()+1);
System.out.println("a3=>"+stampedReference.getStamp());
},"a").start();
new Thread(()->{
int stamp = stampedReference.getStamp();
System.out.println("b1=>"+stamp);
//a、b线程在这里一起休眠两秒
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
stampedReference.compareAndSet(1, 6, stampedReference.getStamp(), stampedReference.getStamp()+1);
System.out.println("b2=>"+stampedReference.getStamp());
},"b").start();
}
}
二十一、各种锁的理解
21.1、公平锁和非公平锁
公平锁:非常公平,不允许插队
非公平锁:不公平,可以根据情况来插队;例如,线程A需要3秒,线程B需要1小时
21.2、可重入锁
可重入锁(递归锁)
大部分锁默认的都是非公平锁
可重入锁(synchronized版):
package com.my.lock;
import java.util.concurrent.TimeUnit;
//可重入锁:synchronized版
public class Test01 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
try {
phone.send();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
try {
phone.send();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"B").start();
}
}
class Phone{
public synchronized void send() throws InterruptedException {
System.out.println(Thread.currentThread().getName()+"=>发短信");
call();
}
public synchronized void call() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"=>打电话");
}
}
可重入锁(Lock版):
package com.my.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//可重入锁:Lock版
public class Test02 {
public static void main(String[] args) {
Phone02 phone = new Phone02();
new Thread(()->{
phone.send();
},"A").start();
new Thread(()->{
phone.send();
},"B").start();
}
}
class Phone02{
Lock lock = new ReentrantLock();
public void send() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"=>发短信");
call();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void call() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"=>打电话");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
21.3、自旋锁
自旋锁核心:循环+CAS
自定义创建自旋锁:
package com.my.lock;
import java.util.concurrent.atomic.AtomicReference;
//定义自旋锁:循环+CAS
public class Test03 {
AtomicReference atomicReference = new AtomicReference();
//加锁
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"线程:进入了我的自旋锁!");
//就是如果当前线程不是“null”,就会一直循环
while (!atomicReference.compareAndSet(null,thread)){}
}
//解锁
public void myUnLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"线程:拿到了锁,开始释放锁!");
atomicReference.compareAndSet(thread,null);
}
}
调用自定义的自旋锁:
package com.my.lock;
import java.util.concurrent.TimeUnit;
//调用自己创建的自旋锁
public class Test04 {
public static void main(String[] args) {
Test03 lock = new Test03();
new Thread(()->{
lock.myLock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.myUnLock();
},"A").start();
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"线程:正在尝试获取锁!");
lock.myLock();
lock.myUnLock();
},"B").start();
}
}
21.4、死锁
package com.my.lock;
import java.util.concurrent.TimeUnit;
//搭建“死锁”现场
public class Test05 {
public static void main(String[] args) {
String LockA = "LockA";
String LockB = "LockB";
new Thread(new MyThread(LockA,LockB),"A线程").start();
new Thread(new MyThread(LockB,LockA),"B线程").start();
}
}
class MyThread implements Runnable{
private String LockA;
private String LockB;
public MyThread(String lockA, String lockB) {
LockA = lockA;
LockB = lockB;
}
@Override
public void run() {
synchronized (LockA){
System.out.println(Thread.currentThread().getName()+":拿着LockA,试图想要LockB!");
//休眠一会,保证线程B,那到B锁
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (LockB){
System.out.println(Thread.currentThread().getName()+":拿着LockB,试图想要LockA!");
}
}
}
}
// 多态, 在JAVA中是这样用的, 其实在PHP当中可以自然消除, 因为参数是动态的, 你传什么过来都可以, 不限制类型, 直接调用类的方法
abstract class Tiger {
public abstract function climb();
}
class XTiger extends Tiger {
public function climb()
jQuery.extend({
handleError: function( s, xhr, status, e ) {
// If a local callback was specified, fire it
if ( s.error ) {
s.error.call( s.context || s, xhr, status, e );
}
always 总是
rice 水稻,米饭
before 在...之前
live 生活,居住
usual 通常的
early 早的
begin 开始
month 月份
year 年
last 最后的
east 东方的
high 高的
far 远的
window 窗户
world 世界
than 比...更
最近使用mybatis.3.1.0时无意中碰到一个问题:
The errors below were detected when validating the file "mybatis-3-mapper.dtd" via the file "account-mapper.xml". In most cases these errors can be d