JUC并发编程学习
八锁机制
第一种以及第二种:锁的是方法调用者两个方法用的同一个锁
import java.util.concurrent.TimeUnit;
public class mulThread {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
},"B").start();
}
}
class Phone{
public synchronized void sendSms(){
/*try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}

先发短息,然后进行四秒等待,出现打电话
第三种,同步方法以及非同步方法
import java.util.concurrent.TimeUnit;
public class mulThread {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.hello();
},"B").start();
}
}
class Phone{
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
public void hello(){
System.out.printf("hello");
}
}
先输入hello,等四秒输出短信
第四种:两个对象
import java.util.concurrent.TimeUnit;
public class mulThread {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone1.call();
},"B").start();
}
}
class Phone{
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
public void hello(){
System.out.printf("hello");
}
}
第五种:两个静态同步方法
import java.util.concurrent.TimeUnit;
public class mulThread {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
},"B").start();
}
}
class Phone{
//类已加载就处理static,锁的Class
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
public void hello(){
System.out.printf("hello");
}
}
已经锁住class,所以只有一个锁
import java.util.concurrent.TimeUnit;
public class mulThread {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone1.call();
},"B").start();
}
}
class Phone{
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
public void hello(){
System.out.printf("hello");
}
}
一个对象,ji一个静态同步锁,普通同步锁
不是同一个锁
import java.util.concurrent.TimeUnit;
public class mulThread {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
},"B").start();
}
}
class Phone{
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
public void hello(){
System.out.printf("hello");
}
}
两个对象,一个静态同步锁,普通同步锁
import java.util.concurrent.TimeUnit;
public class mulThread {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone1.call();
},"B").start();
}
}
class Phone{
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
public void hello(){
System.out.printf("hello");
}
}
集合的不安全
list不安全
set不安全
hashMap不安全
callable
代码测试:
package callabletest;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
new Thread().start();
MyThread myThread = new MyThread();
FutureTask task = new FutureTask(myThread);
new Thread(task,"A").start();
//get方法可能会造成阻塞
Object o = task.get();
System.out.println(o);
}
}
class MyThread implements Callable{
@Override
public String call() throws Exception {
return "null";
}
}
辅助类
CountDownLatch
package add;
import java.util.concurrent.CountDownLatch;
public class count {
public static void main(String[] args) throws InterruptedException {
CountDownLatch count = new CountDownLatch(6);
for (int i = 1; i <=6 ; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"out");
count.countDown();
}, String.valueOf(i)).start();
}
count.await();//等待归零,然后向下执行
System.out.println("关门");
}
}
原理:
count.countDown()
count.await();//等待归零,然后向下执行
每次又线程调用就减一,假设计数器变为0,countDownLatch。await()就会被唤醒,继续执行。
cycliBarrier
package add;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class adddemo {
public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
CyclicBarrier barrier = new CyclicBarrier(7,()->{
System.out.println("成功");
});
for (int i = 1; i <=7 ; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"show");
try {
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
Semaphore
一个计数信号量
一般作用于限流
package add;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <=6 ; i++) {
new Thread(()->{
//acquire 得到
//
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();
}
}, String.valueOf(i)).start();
}
}
}
原理:
semaphore.acquire();
semaphore.release();
ReadWriteLock
特点:读可以多线程进行,但是写只能一次
package rw;
import sun.font.FontRunIterator;
import java.time.temporal.ValueRange;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class demo {
public static void main(String[] args) {
Mycache mycache = new Mycache();
for (int i = 1; i <=5 ; i++) {
final int temp=i;
new Thread(()->{
mycache.put(temp+"",temp+"");
},String.valueOf(temp)).start();
}
for (int i = 1; i <=5 ; i++) {
final int temp=i;
new Thread(()->{
mycache.get(temp+"");
},String.valueOf(temp)).start();
}
}
}
/*
自定义缓存
*/
class Mycache{
private volatile Map<String,Object> map=new HashMap<>();
private ReadWriteLock readWriteLock=new ReentrantReadWriteLock();
//写的时候只希望同时只有一个线程写
public void put(String key,Object v){
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"写入"+key);
map.put(key, v);
System.out.println(Thread.currentThread().getName()+"写入ok");
} 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");
} finally {
readWriteLock.readLock().unlock();
}
}
}
阻塞队列

测试:
package bq;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class Test {
public static void main(String[] args) {
test1();
}
/*
抛出异常
*/
public static void test1(){
ArrayBlockingQueue queue = new ArrayBlockingQueue<>(3);
System.out.println(queue.add("a"));
System.out.println(queue.add("b"));
System.out.println(queue.add("c"));
System.out.println(queue.add("d"));
}
}
会抛出异常
package bq;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class Test {
public static void main(String[] args) {
test1();
}
/*
抛出异常
*/
public static void test1(){
ArrayBlockingQueue queue = new ArrayBlockingQueue<>(3);
System.out.println(queue.add("a"));
System.out.println(queue.add("b"));
System.out.println(queue.add("c"));
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
}
}

| 方式 | 抛出异常 | 有返回值,不抛异常 | 阻塞 等待 | 超时等待 |
|---|---|---|---|---|
| 添加 | add | offer | put | offer |
| 移除 | remove | poll | take | poll |
| 检查队首元素 | element | peek | - | - |
SynchronusQueue
没有容量,只有取出来才能放进去下一个
测试:
package bq;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) {
BlockingQueue<String> objects = new SynchronousQueue<>();
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+"put->1");
objects.put("1");
System.out.println(Thread.currentThread().getName()+"put->2");
objects.put("2");
System.out.println(Thread.currentThread().getName()+"put->3");
objects.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+"-->"+objects.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"-->"+objects.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"-->"+objects.take());
TimeUnit.SECONDS.sleep(3);
}catch (InterruptedException e){
e.printStackTrace();
}
}).start();
}
}
线程池
好处:
- 降低资源的消耗
- 提高响应的速度
- 方便管理
线程复用,可以控制最大的并发数,管理线程
三大方法
线程池不允许使用Excutors去创建,而是通过ThreadPoolExecutor的方式,这样的处理让更明确线程池的运行规则,规避资源耗尽的风险。

package bq;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// Executors 工具类、3大方法
public class Pool {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor();// 单个线程
// ExecutorService threadPool = Executors.newFixedThreadPool(5); // 创建一个固定的线程池的大小
// ExecutorService threadPool = Executors.newCachedThreadPool(); // 可伸缩的,遇强则强,遇弱则弱
try {
for (int i = 0; i < 100; i++) {
// 使用了线程池之后,使用线程池来创建线程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
七大参数
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,//核心线程数
int maximumPoolSize,//最大核心线程池大小
long keepAliveTime,//最大存活时间
TimeUnit unit,//超时单位
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,//阻塞队列
Executors.defaultThreadFactory()//线程工厂
, defaultHandler);//拒绝策略
}
创建线程池
拒绝策略
四大函数式编程
函数式接口:有且只有一个接口
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
//简化编程模型,在新版本的信息底层

代码测试:
Function
public class Fun {
public static void main(String[] args) {
Function function = new Function<String, String>() {
@Override
public String apply(String s) {
return s;
}
};
System.out.println(function.apply("abc"));
}
}
@FunctionalInterface
public interface Function<T, R> {//传入t类型,返回R类型
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
predicate断定型接口
//断定型接口:有一个输入参数,返回值只能是布尔值
public class predi {
public static void main(String[] args) {
Predicate<String> predicate = new Predicate<String>() {
@Override
public boolean test(String n) {
return false;
}
};
}
}
简化:
public class predi {
public static void main(String[] args) {
Predicate<String> predicate = (str)->{
return str.isEmpty();
};
System.out.println(predicate.test(""));
}
}
消费型接口consumer:只有输入,没有返回值
public class com {
public static void main(String[] args) {
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String str) {
System.out.println(str);
}
};
consumer.accept("adsad");
}
}
源码:
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
Supplier供给型接口:没有参数,只有返回值
源码
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
测试:
public class sup {
public static void main(String[] args) {
Supplier syn=new Supplier() {
@Override
public String get() {
return "null";
}
};
System.out.println(syn.get());
}
}
Stream流式计算
what?
测试:
package strem;
import org.omg.PortableServer.LIFESPAN_POLICY_ID;
import java.util.Arrays;
import java.util.List;
public class test {
/*
1.id为偶数
2.年龄大于23
3.大写字母
4.倒着排序
5,只有一个用户
*/
public static void main(String[] args) {
User a1 = new User(1, "A", 21);
User a2 = new User(2, "B", 22);
User a3 = new User(3, "C", 23);
User a4 = new User(4, "D", 24);
User a5 = new User(5, "E", 25);
List<User> list= Arrays.asList(a1,a2,a3,a4,a5);
//计算
//链式编程
list.stream()
.filter(u-> u.getId()%2!=0)
.filter(u->u.getAge()>=23)
.map(u->u.getName().toLowerCase())
.sorted((u,u11)->{return u11.compareTo(u);})
.limit(1)
.forEach(System.out::println);
}
}
class User{
private int id;
private String name;
private int age;
public User(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
ForkJoin
what?
forkjoin在jdk1.7 并行执行任务,提高效率,大数据量
特点:工作窃取
异步回调
JMM
volatile
volatile是java虚拟机提供的轻量级的同步机制
- 保证可见性
- 不保证原子性
- 禁止指令重排
JMM
java内存模型,不存在的东西,概念,约定!
关于JMM的一些同步的约定:
1. 线程解锁前,必须把共享变量立刻
2.

内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)
-
- 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操作之前,必须把此变量同步回主内存
可见性
package demo;
import java.util.concurrent.TimeUnit;
public class JMM {
private volatile static int num=0;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while (num==0){
}
}).start();
TimeUnit.SECONDS.sleep(2);
num=1;
System.out.println(num);
}
}
可以保证可见性
不保证原子性
原子性:不可分割,线程A在执行任务的时候,不能被打扰的,也不能被分割,要么t同时失败,要么成功
如果不加lock和synchronized,可以使用什么?
可以使用原子类的包装类
这些类的底层都直接和操作系统挂勾,在内存中修改值,unsafe类是一个很特殊的存在。
指令重排
源代码-》编译器优化的重排-》指令并行也可能会重排-》内存系统也会重排-》执行。
volatile可以避免指令重排 :
内存屏障,cpu指令,作用:
1.保证特点的操作的执行顺序
2.可以保证某些变量的内存可见性
Volatile是可以保持可见性,不能保证原子性,由于内存屏障,可以保证避免指令重排现象。
单例模式
饿汉式
public class hungry {
private hungry(){
}
private final static hungry HUNGRY=new hungry();
public static hungry getInstance(){
return HUNGRY;
}
}
懒汉式
package model;
public class Lazy {
private Lazy(){}
private static Lazy lazy;
public static Lazy getInstance(){
if (lazy==null){
lazy=new Lazy();
}
return lazy;
}
}
普通懒汉式,在单线程的环境能够完美处理,但是在多线程的环境下,却不能保证。
双重检测模式的懒汉式 DCL懒汉式
package model;
public class Lazy {
private Lazy(){}
private volatile static Lazy lazy;
public static Lazy getInstance(){
if (lazy==null){
synchronized (Lazy.class){
if(lazy==null){
if(lazy==null){
lazy=new Lazy();//不是原子性操作
}
}
}
}
return lazy;
}
}
反射可以破坏
package model;
import sun.applet.Main;
import javax.swing.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Lazy {
private Lazy(){}
private volatile static Lazy lazy;
public static Lazy getInstance(){
if (lazy==null){
synchronized (Lazy.class){
if(lazy==null){
if(lazy==null){
lazy=new Lazy();
}
}
}
}
return lazy;
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Lazy instance = Lazy.getInstance();
Constructor<Lazy> constructor = Lazy.class.getDeclaredConstructor();
constructor.setAccessible(true);
Lazy lazy1 = constructor.newInstance();
System.out.println(instance.hashCode());
System.out.println(lazy1.hashCode());
}
}
解决方法:
构造方法加入
if(lazy!=null){
throw new RuntimeException("error");
}
但是如果没有使用单例的模式创建,而是使用两次反射创建,则又会失效:
穿插静态内部类
package model;
public class Holder {
private Holder(){}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER=new Holder();
}
}
为了解决反射破坏单例的情况,我们发现枚举可以保证唯一
枚举
package model;
import javax.sound.midi.Soundbank;
//enum本身也是一个类
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class test{
public static void main(String[] args) {
EnumSingle instance = EnumSingle.INSTANCE;
EnumSingle instance1 = EnumSingle.INSTANCE;
System.out.println(instance==instance1);
}
}
枚举的u源码

浙公网安备 33010602011771号