并发编程(下)
1.共享模型之内存
1.1 java内存模型
1.2 可见性
-
原因分析
-
一个线程对主存对数据进行修改 对另外一个线程是不可见的
-
解决方法 使用volatile
-
可见性 vs 原子性
-
因为println加了synchronized
1.3 同步模式之Baliking
@Slf4j(topic = "c.test2")
public class Test2 {
public static void main(String[] args) {
TwoPhaseTermination t1 = new TwoPhaseTermination();
// 重复调用2次start方法 不产生2个线程
t1.start();
t1.start();
Sleeper.sleep(4);
log.debug("停止监控线程");
t1.stop();
}
}
@Slf4j(topic = "c.TwoPhaseTermination")
class TwoPhaseTermination {
// 监控线程
private Thread thread;
private volatile boolean stop = false;
// 用于判断是否执行过start方法
private boolean starting = false;
public void start() {
synchronized (this) {
if (starting) {
return;
}
starting = true;
}
thread = new Thread(() -> {
while (true) {
if (stop) {
log.debug("料理后事");
break;
}
try {
Thread.sleep(1000);
log.debug("执行监控记录");
} catch (Exception e) {
}
}
});
thread.start();
}
public void stop() {
stop = true;
thread.interrupt();
}
}
1.4 指令重排序
-
有序性
-
指令重排序优化
- 结论:在不改变程序结果的前提下,这些指令的各个阶段可以通过重排序和组合来实现指令级并行,指令重排的前提是,重排指令不能影响结果
1.5 volatile原理
1.如何保证可见性
2.如何保证有序性
-
volatile不能保证原子性
3.double-checked-locking
-
问题
- 解决
@Slf4j(topic = "c.test3")
public class Test3 {
public Test3() {
}
// volatile 防止指令重排序
private static volatile Test3 INSTANCE = null;
public static Test3 getInstance() {
// 首次判断为不为空
if (INSTANCE == null) {
synchronized (Test3.class) {
// t1 t2 两个线程 只能有一个线程进入代码块 去初始化对象
if (INSTANCE == null) {
INSTANCE = new Test3();
}
}
}
return INSTANCE;
}
}
4.happens-before
2.共享模型之无锁
1.CAS
- 无锁实现对共享变量操作
@Slf4j(topic = "c.test4")
public class Test4 {
public static void main(String[] args) {
AccountCas cas = new AccountCas(10000);
Account.demo(cas);
}
}
class AccountCas implements Account {
private AtomicInteger atomicInteger;
public AccountCas(int a) {
AtomicInteger c = new AtomicInteger(a);
this.atomicInteger = c;
}
@Override
public Integer getBalance() {
return atomicInteger.get();
}
@Override
public void withdraw(Integer account) {
while (true) {
int pre = atomicInteger.get();
// cas 自旋操作
int update = pre - account;
if (atomicInteger.compareAndSet(pre, update)) {
break;
}
}
}
}
interface Account {
// 获取余额
Integer getBalance();
// 取款
void withdraw(Integer account);
/**
* 启动1000个线程 进行取款操作
*
* @param account
*/
static void demo(Account account) {
List<Thread> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
list.add(new Thread(() -> {
account.withdraw(10);
}));
}
// 耗时
long startTime = System.currentTimeMillis();
list.forEach(Thread::start);
// 等待全部线程结束
list.forEach(thread -> {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
long endTime = System.currentTimeMillis();
System.out.println(account.getBalance() + "cost:" + "耗时:" + (endTime - startTime) + "ms");
}
}
- cas工作原理
- 为什么无锁效率高
- CAS的特点
2 .原子整数
- AtomicInteger
3. 原子引用
@Slf4j(topic = "c.test5")
public class Test5 {
public static void main(String[] args) {
// BigDecimal赋值必须是字符串 如果是浮点数还是会有精度损失
AccountReferenceImpl reference = new AccountReferenceImpl(new BigDecimal("10000"));
AccountReference.demo(reference);
}
}
class AccountReferenceImpl implements AccountReference {
private AtomicReference<BigDecimal> atomicReference;
public AccountReferenceImpl(BigDecimal bigDecimal) {
AtomicReference<BigDecimal> atomicReference = new AtomicReference<>(bigDecimal);
this.atomicReference = atomicReference;
}
@Override
public BigDecimal getBalance() {
return atomicReference.get();
}
@Override
public void withdraw(BigDecimal account) {
while (true) {
BigDecimal pre = atomicReference.get();
// cas 自旋操作
BigDecimal update = pre.subtract(account);
if (atomicReference.compareAndSet(pre, update)) {
break;
}
}
}
}
interface AccountReference {
// 获取余额
BigDecimal getBalance();
// 取款
void withdraw(BigDecimal account);
/**
* 启动1000个线程 进行取款操作
*
* @param account
*/
static void demo(AccountReference account) {
List<Thread> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
list.add(new Thread(() -> {
account.withdraw(BigDecimal.TEN);
}));
}
// 耗时
long startTime = System.currentTimeMillis();
list.forEach(Thread::start);
// 等待全部线程结束
list.forEach(thread -> {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
long endTime = System.currentTimeMillis();
System.out.println(account.getBalance() + "cost:" + "耗时:" + (endTime - startTime) + "ms");
}
}
-
ABA问题
@Slf4j(topic = "c.test6") public class Test6 { private static AtomicReference<String> ref = new AtomicReference<>("A"); public static void main(String[] args) { log.debug("main start.."); String pre = ref.get(); other(); Sleeper.sleep(2); log.debug("changeA->C {}", ref.compareAndSet(pre, "C")); } public static void other() { new Thread(() -> { log.debug("changeA->B {}", ref.compareAndSet(ref.get(), "B")); }, "t1").start(); Sleeper.sleep(1); new Thread(() -> { log.debug("changeB->A {}", ref.compareAndSet(ref.get(), "A")); }, "t2").start(); } }
-
所谓ABA问题也就是说主线程要修改的值 被其他线程先修改了 不过其它线程又改成了主线程最开始的值,但这对于主线程是无感知的
-
解决 使用AtomicStampedReference加版本号的方式 来判断是否被其它线程修改过
@Slf4j(topic = "c.test6") public class Test6 { private static AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0); public static void main(String[] args) { log.debug("main start.."); String pre = ref.getReference(); int stamp = ref.getStamp(); log.debug("main版本号为:{}", stamp); other(); Sleeper.sleep(2); log.debug("main休眠后 版本号为:{}",ref.getStamp()); log.debug("changeA->C {}", ref.compareAndSet(pre, "C", stamp, stamp + 1)); } public static void other() { new Thread(() -> { log.debug("changeA->B {}", ref.compareAndSet(ref.getReference(), "B", ref.getStamp(), ref.getStamp() + 1)); log.debug("t1修改后版本号为:{}", ref.getStamp()); }, "t1").start(); Sleeper.sleep(1); new Thread(() -> { log.debug("changeB->A {}", ref.compareAndSet(ref.getReference(), "A", ref.getStamp(), ref.getStamp() + 1)); log.debug("t2修改后版本号为:{}", ref.getStamp()); }, "t2").start(); } }
-
需求 我不关心版本号有没有改变 我只关心值有没有被其它线程修改过?
@Slf4j(topic = "c.test7") public class Test7 { public static void main(String[] args) { AtomicMarkableReference<GarbageBag> reference = new AtomicMarkableReference<>(new GarbageBag("装满了垃圾"), true); log.debug("start..."); GarbageBag pre = reference.getReference(); log.debug(pre.toString()); new Thread(() -> { log.debug("t1线程想更换垃圾袋子"); // expectedMark—期望值 // newMark -标记的新值 boolean andSet = reference.compareAndSet(pre, new GarbageBag("t1空垃圾袋子"), true, true); log.debug("t1线程更换成功了没:{}", andSet); }, "t1").start(); Sleeper.sleep(1); log.debug("想换一个新的垃圾袋"); boolean result = reference.compareAndSet(pre, new GarbageBag("空垃圾袋"), true, true); log.debug("换成功了没:{}", result); log.debug(reference.getReference().toString()); } } class GarbageBag { private String name; public GarbageBag(String name) { this.name = name; } public String getName() { return name; } @Override public String toString() { return "垃圾袋{" + "name='" + name + '\'' + '}'; } }
4.原子数组
@Slf4j(topic = "c.test8")
public class Test8 {
public static void main(String[] args) {
demo(
() -> new int[10],
(arr) -> arr.length,
(arr, index) -> arr[index]++,
(arr) -> System.out.println(Arrays.toString(arr))
);
}
// 参数1提供数组
// 参数2数组长度
// 自增方法
// 打印
private static <T> void demo(Supplier<T> arrSupplier,
Function<T, Integer> arrFunction,
BiConsumer<T, Integer> arrBiConsumer,
Consumer<T> arrConsumer
) {
List<Thread> list = new ArrayList<>();
T arr = arrSupplier.get();
// 获取数组长度
Integer arrLength = arrFunction.apply(arr);
for (int i = 0; i < arrLength; i++) {
list.add(new Thread(() -> {
for (int j = 0; j < 1000; j++) {
arrBiConsumer.accept(arr, j % arrLength);
}
}));
}
list.forEach(Thread::start);
list.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
arrConsumer.accept(arr);
}
}
- 多线程下数组元素是没有安全性的
- 解决使用AtomicIntegerArray
demo(
() -> new AtomicIntegerArray(10),
(arr) -> arr.length(),
(arr, index) -> arr.incrementAndGet(index),
(arr) -> System.out.println(arr)
);
5.字段更新器
@Slf4j(topic = "c.test9")
public class Test9 {
public static void main(String[] args) {
User user = new User();
AtomicReferenceFieldUpdater atomicReferenceFieldUpdater = AtomicReferenceFieldUpdater
.newUpdater(User.class, String.class, "name");
System.out.println(atomicReferenceFieldUpdater.compareAndSet(user, null, "张三"));
System.out.println(user);
}
}
class User {
// cas操作需要变量被volatile修饰
volatile String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
6.原子累加器
- LongAdder
@Slf4j(topic = "c.test10")
public class Test10 {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
demo(() -> new AtomicLong(0), (arr) -> arr.incrementAndGet());
}
for (int i = 0; i < 5; i++) {
demo(() -> new LongAdder(), (arr) -> arr.increment());
}
}
private static <T> void demo(Supplier<T> arrSupplier,
Consumer<T> arrFunction
) {
List<Thread> list = new ArrayList<>();
T arr = arrSupplier.get();
// 获取数组长度
for (int i = 0; i < 4; i++) {
list.add(new Thread(() -> {
for (int j = 0; j < 50000; j++) {
arrFunction.accept(arr);
}
}));
}
long startTime = System.currentTimeMillis();
list.forEach(Thread::start);
list.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
long endTime = System.currentTimeMillis() - startTime;
System.out.println(arr + "cost:" + endTime);
}
}
源码之LongAdder
- add方法
-
longAccumulate方法
-
cells数组存在 但线程的cell对象没创建
-
最后一种情况 cells 和cell都存在 进行cas操作
-
sum求和
6.Unsafe
-
原子整数底层也是用unsafe实现的
@Slf4j(topic = "c.test12") public class Test12 { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); // 设置允许访问私有变量 theUnsafe.setAccessible(true); // 因为unsafe被修饰static所以同属于类 不同属于对象 所有不需要传递对象 Unsafe unsafe = (Unsafe) theUnsafe.get(null); // unsafe实现cas操作 Teacher thread = new Teacher(); // 获取id和name的offset long id = unsafe.objectFieldOffset(Teacher.class.getDeclaredField("id")); long name = unsafe.objectFieldOffset(Teacher.class.getDeclaredField("name")); unsafe.compareAndSwapInt(thread, id, 0, 1); unsafe.compareAndSwapObject(thread, name, null, "sb"); System.out.println(thread); } } @Data class Teacher { public volatile int id; public volatile String name; }
-
使用unsafe实现MyAtomicIntger
@Slf4j(topic = "c.test13") public class Test13 { public static void main(String[] args) { Account.demo(new MyAtomicInt(10000)); } } class MyAtomicInt implements Account { private volatile int value; private static final long VALUEOFFSET; private static final Unsafe UNSAFE; static { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); // 因为unsafe被修饰static所以同属于类 不同属于对象 所有不需要传递对象 UNSAFE = (Unsafe) theUnsafe.get(null); VALUEOFFSET = UNSAFE.objectFieldOffset(MyAtomicInt.class.getDeclaredField("value")); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(); } } public int getValue() { return value; } public MyAtomicInt(int value) { this.value = value; } public void decrement(int account) { while (true) { int pre = this.value; int update = pre - account; if (UNSAFE.compareAndSwapInt(this, VALUEOFFSET, pre, update)) { break; } } } @Override public Integer getBalance() { return getValue(); } @Override public void withdraw(Integer account) { decrement(account); } }
3.共享模型之不可变
-
由于simpledatefomat不是线程安全的
1.不可变类设计
-
final作用
属性使用了final保证改属性是只读的,不能修改
保证了类中的方法不被覆盖,反正子类无意修改破坏不可变性
-
保护性拷贝
final只能保证数组类型的引用不被修改,那如果我修改数组里面的值呢?看看string是怎么做的
2.享元模式
-
为了解决保护性拷贝每次创建新对象而占用内存的情况
-
体现
3.实现自定义线程池
@Slf4j(topic = "c.test14")
public class Test14 {
public static void main(String[] args) {
Pool pool = new Pool(2);
for (int i = 0; i < 5; i++) {
new Thread(() -> {
Connection borrow = pool.borrow();
Sleeper.sleep(1);
pool.free(borrow);
}).start();
}
}
}
@Slf4j(topic = "c.pool")
class Pool {
private int poolSize;
// 连接对象数组
private Connection[] connections;
// 数组表示连接池状态 0空闲
private AtomicIntegerArray status;
public Pool(int poolSize) {
this.poolSize = poolSize;
this.connections = new Connection[poolSize];
this.status = new AtomicIntegerArray(new int[poolSize]);
for (int i = 0; i < connections.length; i++) {
connections[i] = new MockConnection("连接" + i);
}
}
// 获取连接
public Connection borrow() {
while (true) {
for (int i = 0; i < poolSize; i++) {
if (status.get(i) == 0) {
if (status.compareAndSet(i, 0, 1)) {
log.debug("borrow:{}", connections[i]);
return connections[i];
}
}
}
synchronized (this) {
try {
log.debug("wait...");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 归还连接
public void free(Connection connection) {
for (int i = 0; i < poolSize; i++) {
if (connections[i] == connection) {
status.set(i, 0);
synchronized (this) {
log.debug("free:{}", connection);
this.notifyAll();
}
break;
}
}
}
}
class MockConnection implements Connection {
private String name;
public MockConnection(String name) {
this.name = name;
}
@Override
public String toString() {
return "MockConnection{" +
"name='" + name + '\'' +
'}';
}
}
4.final原理
`
- final获取变量的原理
UseFinal1类中会将A变量复制一份到自已的栈内存中
如果没加final会去TestFinal类中去找A变量,走的是共享内存
4.自定义线程池
4.1 自定义阻塞队列
class BlockQueue<T> {
private Deque<T> deque = new ArrayDeque<>();
// 锁
private ReentrantLock lock = new ReentrantLock();
// 生产者条件变量
private Condition fullWaitSet = lock.newCondition();
// 消费者条件变量
private Condition emptyWaitSet = lock.newCondition();
// 容量
private int capacity;
public T poll(long time, TimeUnit unit) {
lock.lock();
long nanos = unit.toNanos(time);
try {
while (deque.isEmpty()) {
try {
if (nanos <= 0) {
return null; // 防止虚假唤醒
}
nanos = emptyWaitSet.awaitNanos(nanos);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
T t = deque.removeFirst();
fullWaitSet.signal();
return t;
} finally {
lock.unlock();
}
}
// 阻塞获取
public T task() {
lock.lock();
try {
while (deque.isEmpty()) {
try {
emptyWaitSet.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
T t = deque.removeFirst();
fullWaitSet.signal();
return t;
} finally {
lock.unlock();
}
}
// 阻塞添加
public void put(T element) {
lock.lock();
try {
while (deque.size() == capacity) {
try {
fullWaitSet.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
deque.addLast(element);
emptyWaitSet.signal();
} finally {
lock.unlock();
}
}
// 获取队列大小
public int size() {
lock.lock();
try {
return deque.size();
} finally {
lock.unlock();
}
}
}
4.2 ThreadPoolExecutor
1.线程池状态
2.构造方法
4.3 创建一个固定线程的线程池 Executors.newFixedThreadPool()
@Slf4j(topic = "c.test2")
public class Test2 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(() -> {
log.debug("1");
});
executorService.execute(() -> {
log.debug("2");
});
executorService.execute(() -> {
log.debug("3");
});
}
}
- 可以看到newFixedThreadPool方法中 生存时间为0 也就是说创建的线程池不会拥有救急线程 只会有核心线程,就算没有任务时也不会一直运行
4.4 带缓冲功能的线程池 Executors.newCachedThreadPool()
@Slf4j(topic = "c.test3")
public class Test3 {
public static void main(String[] args) {
SynchronousQueue<Integer> queue = new SynchronousQueue<>();
new Thread(() -> {
try {
log.debug("put:{}", 1);
queue.put(1);
log.debug("putted:{}", 1);
log.debug("put:{}", 2);
queue.put(2);
log.debug("putted:{}", 2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
Sleeper.sleep(1);
new Thread(() -> {
try {
log.debug("task:{}", 1);
queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
Sleeper.sleep(1);
new Thread(() -> {
try {
log.debug("task:{}", 2);
queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
4.5 单线程线程池 Executors.newSingleThreadExecutor()
@Slf4j(topic = "c.test4")
public class Test4 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(() -> {
log.debug("by/zero");
int i = 1 / 0;
});
Sleeper.sleep(1);
executorService.execute(() -> {
log.debug("2");
});
Sleeper.sleep(1);
executorService.execute(() -> {
log.debug("3");
});
}
}
- 可以看到 当线程1发生异常后 会从线程池创建一个新的线程强执行其它任务
5.提交任务
@Slf4j(topic = "c.test5")
public class Test5 {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
Future<String> future = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
log.debug("running...");
Sleeper.sleep(3);
return "Ok";
}
});
String s = future.get();
log.debug("result:{}",s);
}
}
@Slf4j(topic = "c.test6")
public class Test6 {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(2);
List<Future<Object>> futureList = executorService.invokeAll(Arrays.asList(
() -> {
log.debug("start ....1");
Sleeper.sleep(2);
return "1";
},
() -> {
log.debug("start ....2");
Sleeper.sleep(2);
return "2";
},
() -> {
log.debug("start ....3");
Sleeper.sleep(3);
return "3";
}
));
futureList.forEach(future -> {
try {
String result = (String) future.get();
log.debug("result:{}", result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
});
}
}
6.关闭线程
-
其他方法
@Slf4j(topic = "c.test7")
public class Test7 {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(2);
Future<String> future1 = executorService.submit(() -> {
log.debug("start ....1");
Sleeper.sleep(1);
log.debug("ok 1");
return "1";
});
Future<String> future2 = executorService.submit(() -> {
log.debug("start ....2");
Sleeper.sleep(1);
log.debug("ok 2");
return "2";
});
Future<String> future3 = executorService.submit(() -> {
log.debug("start ....3");
Sleeper.sleep(1);
log.debug("ok 3");
return "3";
});
log.debug("shutdown");
executorService.shutdown();
executorService.awaitTermination(3, TimeUnit.SECONDS);
log.debug("await...");
}
}
7.饥饿
@Slf4j(topic = "c.test8")
public class Test8 {
private static final List<String> MENU = Arrays.asList("套餐1", "套餐2", "套餐3", "套餐4", "套餐5");
static Random random = new Random();
static String choose() {
return MENU.get(random.nextInt(MENU.size()));
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(() -> {
log.debug("处理点餐");
Future<String> future = executorService.submit(() -> {
log.debug("做菜");
return choose();
});
try {
log.debug("上菜:{}",future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
});
executorService.execute(() -> {
log.debug("处理点餐");
Future<String> future = executorService.submit(() -> {
log.debug("做菜");
return choose();
});
try {
log.debug("上菜:{}",future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
});
}
}
-
解决
// 点餐线程只处理点餐 做菜线程只处理做菜 ExecutorService executorService = Executors.newFixedThreadPool(1); ExecutorService cookieService = Executors.newFixedThreadPool(1);
8.创建合适的线程池参数
9.任务调度线程池
-
Timer
@Slf4j(topic = "c.test9") public class Test9 { public static void main(String[] args) { Timer timer = new Timer(); TimerTask t1 = new TimerTask() { @Override public void run() { log.debug("task 1"); Sleeper.sleep(2); } }; TimerTask t2 = new TimerTask() { @Override public void run() { log.debug("task 2"); Sleeper.sleep(1); } }; log.debug("start.."); // timer添加2个任务,delay延迟多久执行 timer.schedule(t1,1); timer.schedule(t2,1); } }
-
ScheduledExecutorService
@Slf4j(topic = "c.test10") public class Test10 { public static void main(String[] args) { ScheduledExecutorService pool = Executors.newScheduledThreadPool(2); pool.schedule(() -> { log.debug("task 1"); }, 1, TimeUnit.SECONDS); pool.schedule(() -> { log.debug("task 2"); }, 1, TimeUnit.SECONDS); } }
// 定时任务 @Slf4j(topic = "c.test10") public class Test10 { public static void main(String[] args) { ScheduledExecutorService pool = Executors.newScheduledThreadPool(1); pool.scheduleAtFixedRate(() -> { log.debug("start..."); }, 1, 1, TimeUnit.SECONDS); } }
@Slf4j(topic = "c.test11")
public class Test11 {
public static void main(String[] args) {
// 每周五晚上11点执行
ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
LocalDateTime now = LocalDateTime.now();
// 将当前时间设置为星期五11点
LocalDateTime dateTime = now.withHour(22).withMinute(30).withSecond(0).withNano(0).with(DayOfWeek.FRIDAY);
// 如果当前时间大于星期五,那么设置到下一周
if (now.compareTo(dateTime) > 0) {
dateTime = dateTime.plusWeeks(1);
}
// 延迟多久执行
long toMillis = Duration.between(now, dateTime).toMillis();
// 隔多久执行一次
long loopTime = 1000 * 60 * 60 * 24 * 7;
pool.scheduleAtFixedRate(() -> {
log.debug("{}", "每周五晚上11点定时执行");
}, toMillis, loopTime, TimeUnit.MILLISECONDS);
}
}
10.线程池处理异常
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.submit(() -> {
try {
log.debug("start...");
int i = 1 / 0;
} catch (Exception e) {
log.error("error...",e);
}
});
Future<Boolean> future = executorService.submit(() -> {
log.debug("start...");
int i = 1 / 0;
return true;
});
log.debug("result:{}",future.get());
11.tomcat线程池
-
tomcat线程池配置
12.Fork/Join
@Slf4j(topic = "c.test12")
public class Test12 {
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool(4);
System.out.println(pool.invoke(new MyJoin(5)));
}
}
@Slf4j(topic = "c.myJoin")
class MyJoin extends RecursiveTask<Integer> {
private int i;
public MyJoin(int i) {
this.i = i;
}
@Override
public String toString() {
return "MyJoin{" +
"i=" + i +
'}';
}
@Override
protected Integer compute() {
if (i == 1) {
log.debug("join(){}", i);
return i;
}
MyJoin join = new MyJoin(i - 1);
join.fork();
log.debug("fork(){}+{}", i, join);
int result = i + join.join();
log.debug("join(){}+{}={}", i, join, result);
return result;
}
}
@Slf4j(topic = "c.test12")
public class Test12 {
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool(2);
System.out.println(pool.invoke(new MyJoin(1,5)));
}
}
@Slf4j(topic = "c.myJoin")
class MyJoin extends RecursiveTask<Integer> {
private int begin;
private int end;
public MyJoin(int begin, int end) {
this.begin = begin;
this.end = end;
}
@Override
public String toString() {
return "MyJoin{" +
"begin=" + begin +
", end=" + end +
'}';
}
@Override
protected Integer compute() {
if (begin == end) {
log.debug("join(){}", begin);
return begin;
}
if (end - begin == 1) {
log.debug("join(){}+{}={}", begin, end, end + begin);
return end + begin;
}
int mid = (end + begin) / 2;
MyJoin myJoin = new MyJoin(begin, mid);// 1 3
myJoin.fork();
MyJoin myJoin1 = new MyJoin(mid + 1, end); // 4 5
myJoin1.fork();
log.debug("fork(){}+{}=?", myJoin, myJoin1);
int result = myJoin.join() + myJoin1.join();
log.debug("join(){}+{}={}", myJoin, myJoin1, result);
return result;
}
}
5.JUC
1.aqs
@Slf4j(topic = "c.test13")
public class Test13 {
public static void main(String[] args) {
MyLock lock = new MyLock();
new Thread(() -> {
lock.lock();
log.debug("getlock");
lock.lock();
log.debug("getlock2...");
try {
log.debug("locking");
Sleeper.sleep(1);
} finally {
log.debug("unlocking");
lock.unlock();
}
},"t1").start();
}
}
// 自定义不可重复锁
class MyLock implements Lock {
MySync mySync = new MySync();
class MySync extends AbstractQueuedSynchronizer {
public MySync() {
}
// 枷锁
@Override
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0, 1)) {
// 设置onwer为当前线程
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 释放锁
@Override
protected boolean tryRelease(int arg) {
setExclusiveOwnerThread(null);
// 保证指令有序性
setState(0);
return true;
}
// 是否持有独占锁
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
public Condition getCondition() {
return new ConditionObject();
}
}
@Override
public void lock() {
mySync.acquire(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
mySync.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
return mySync.tryRelease(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return mySync.tryAcquireNanos(1, unit.toNanos(time));
}
@Override
public void unlock() {
mySync.release(1);
}
@Override
public Condition newCondition() {
return mySync.getCondition();
}
}
2.ReentrantLock原理
2.1 可重入原理
2.2可打断原理
2.3公平锁原理
- 非公平锁实现
-
公平锁实现
2.4条件变量原理
- signal流程
2.5Reentrantlock读写锁
@Slf4j(topic = "c.test14")
public class Test14 {
public static void main(String[] args) {
Lock lock = new Lock();
new Thread(() -> {
lock.write();
}, "t1").start();
new Thread(() -> {
lock.write();
}, "t2").start();
}
}
@Slf4j(topic = "c.lock")
class Lock {
private Object o;
private ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
private ReentrantReadWriteLock.ReadLock r = rw.readLock();
private ReentrantReadWriteLock.WriteLock w = }
public void write() {
log.debug("获取写锁...");
w.lock();
try {
log.debug("写入数据..");
Sleeper.sleep(1);
} finally {
log.debug("释放写锁");
w.unlock();
}
}
}
-
读写锁原理
- 读锁的unlock方法
3.StampedLock
@Slf4j(topic = "c.test11")
public class StampedLockTest {
public static void main(String[] args) {
DataContainer container = new DataContainer(1);
new Thread(() -> {
container.read(1);
}, "t1").start();
new Thread(() -> {
container.read(1);
}, "t2").start();
}
}
@Slf4j(topic = "c.data")
class DataContainer {
private int data;
private final StampedLock lock = new StampedLock();
public DataContainer(int data) {
this.data = data;
}
public int read(int readTime) {
long stamp = lock.tryOptimisticRead();
log.debug("OptimisticReadTime:{}", stamp);
Sleeper.sleep(readTime);
if (lock.validate(stamp)) {
log.debug("read finish..:{}", stamp);
return data;
}
log.debug("updating stamp....{}", stamp);
try {
// 验戳失败 重新获取读锁
stamp = lock.readLock();
log.debug("read lock..{}", stamp);
Sleeper.sleep(readTime);
log.debug("read finish,,,{}", stamp);
return data;
} finally {
log.debug("read unlock..{}", stamp);
lock.unlockRead(stamp);
}
}
public void write(int newData) {
long stamp = lock.writeLock();
log.debug("write lock{}", stamp);
try {
Sleeper.sleep(2);
this.data = newData;
} finally {
log.debug("write unlock...{}", stamp);
lock.unlockWrite(stamp);
}
}
}
- 读读
- 读写
-
缺点
不可重入、不支持条件变量
4.Semphore
-
信号量 用来限制能够同时访问资源的线程上限
@Slf4j(topic = "c.test15") public class Test15 { public static void main(String[] args) { Semaphore semaphore = new Semaphore(3); for (int i = 0; i < 10; i++) { new Thread(() -> { try { semaphore.acquire(); } catch (InterruptedException e) { e.printStackTrace(); } try { log.debug("runing...."); Sleeper.sleep(1); log.debug("end...."); } finally { semaphore.release(); } }, "thread" + i).start(); } } }
-
加锁流程
- 释放锁
5.CountDownLacth
@Slf4j(topic = "c.test16")
public class Test16 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
log.debug("wait...");
new Thread(() -> {
log.debug("t1.start");
Sleeper.sleep(1);
log.debug("t1.finish");
latch.countDown();
}, "t1").start();
new Thread(() -> {
log.debug("t2.start");
Sleeper.sleep(2);
log.debug("t2.finish");
latch.countDown();
}, "t2").start();
new Thread(() -> {
log.debug("t3.start");
Sleeper.sleep(3);
log.debug("t3.finish");
latch.countDown();
}, "t3").start();
latch.await();
log.debug("gg~~");
}
}
-
小案例 lol排位10人等待游戏开始
@Slf4j(topic = "c.test17") public class Test17 { public static void main(String[] args) throws InterruptedException { ExecutorService pool = Executors.newFixedThreadPool(10); String[] arr = new String[10]; Random random = new Random(); CountDownLatch latch = new CountDownLatch(10); for (int k = 0; k < 10; k++) { int j = k; pool.submit(() -> { for (int i = 0; i <= 100; i++) { try { Thread.sleep(random.nextInt(100)); } catch (InterruptedException e) { e.printStackTrace(); } arr[j] = i + "%"; System.out.print("\r" + Arrays.toString(arr)); } latch.countDown(); }); } latch.await(); System.out.print("\n" +" 游戏开始"); } }
6.CyclicBarrier
@Slf4j(topic = "c.test18")
public class Test18 {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(2);
CyclicBarrier barrier = new CyclicBarrier(2, () -> {
log.debug("start...");
});
for (int i = 0; i < 3; i++) {
pool.submit(() -> {
log.debug("cyclic start...");
Sleeper.sleep(1);
try {
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
pool.submit(() -> {
log.debug("cyclic start...");
Sleeper.sleep(2);
try {
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
}
pool.shutdown();
}
}
- 与countdownlacth相比 它的计数器会重置。而count用完就没了
6.线程安全类集合
6.1 jdk7 hashmap死链
6.2 concurentHashMap
-
扩容流程