(源码地址:https://github.com/EduMoral/edu/blob/master/concurrent/src)
1, 同步方法和费同步方法是否可以同时调用
可以!同步方法需要锁,非同步方法不需要锁,既然不需要锁两个方法获取不冲突;
2,对业务写方法加锁,对业务写方法不加锁,容易产生脏读(dirtyRead)
解决脏读的方案 copyOnwrite 牺牲写性能增加读性能
3,一个同步方法调用另一个同步方法,一个线程已拥有某个对象的锁,再次申请的时候任然会得到该对象的锁,也就是说synchronized获得锁是是可重入的
子类的同步方法也可以调用父类的同步方法
4,synchronized 关键字抛出异常会释放锁
5,volatile 关键字使一个变量在多个线程之间可见
volatile不能替代synchonnized,volatile只解决了可见性,synchonnzed既解决了可见性又解决了原子性
6,比较简单的原子性操作问题可以atomInteger 等相关类
7,锁定对象如果的对象的属性发生改变锁没有改变,如果对象的引用指向两一个对象则该锁发生改变
8,不使用字符串作为锁对象,因为有可能用的框架使用的也是该字符串,字符串存在常量池中属于同一个对象,会发生死锁问题;
9,Object.wait() 会释放锁,notify()不会释放锁 使用wait时90%用while,大多数情况下使用notifyAll
10,countdownLanch 门栓
countDownLanch lanch = new CountDownLanch(5);
lanch.await()
每调用countDown() 数值减1;为0时开始执行以下代码
11, reentrantlock用于替代synchronized 由于m1锁定this,只有m1执行完毕的时候,m2才能执行
使用reentrantlock可以完成同样的功能 需要注意的是,必须要必须要必须要手动释放锁(重要的事情说三遍)
使用syn锁定的话如果遇到异常,jvm会自动释放锁,但是lock必须手动释放锁,因此经常在finally中进行锁的释放
使用reentrantlock可以进行“尝试锁定”tryLock,这样无法锁定,或者在指定时间内无法锁定,线程可以决定是否继续等待
使用ReentrantLock还可以调用lockInterruptibly方法,可以对线程interrupt方法做出响应,
在一个线程等待锁的过程中,可以被打断
ReentrantLock还可以指定为公平锁
12,面试题:写一个固定容量同步容器,拥有put和get方法,以及getCount方法,
* 能够支持2个生产者线程以及10个消费者线程的阻塞调用
*
* 使用wait和notify/notifyAll来实现
*
* 使用Lock和Condition来实现
* 对比两种方式,Condition的方式可以更加精确的指定哪些线程被唤醒
*
* @author mashibing
*
package yxxy.c_021;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyContainer2<T> {
final private LinkedList<T> lists = new LinkedList<>();
final private int MAX = 10; //最多10个元素
private int count = 0;
private Lock lock = new ReentrantLock();
private Condition producer = lock.newCondition();
private Condition consumer = lock.newCondition();
public void put(T t) {
try {
lock.lock();
while(lists.size() == MAX) { //想想为什么用while而不是用if?
producer.await();
}
lists.add(t);
++count;
consumer.signalAll(); //通知消费者线程进行消费
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public T get() {
T t = null;
try {
lock.lock();
while(lists.size() == 0) {
consumer.await();
}
t = lists.removeFirst();
count --;
producer.signalAll(); //通知生产者进行生产
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return t;
}
public static void main(String[] args) {
MyContainer2<String> c = new MyContainer2<>();
//启动消费者线程
for(int i=0; i<10; i++) {
new Thread(()->{
for(int j=0; j<5; j++) System.out.println(c.get());
}, "c" + i).start();
}
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//启动生产者线程
for(int i=0; i<2; i++) {
new Thread(()->{
for(int j=0; j<25; j++) c.put(Thread.currentThread().getName() + " " + j);
}, "p" + i).start();
}
}
}
13,面试题:写一个固定容量同步容器,拥有put和get方法,以及getCount方法,
能够支持2个生产者线程以及10个消费者线程的阻塞调用
使用wait和notify/notifyAll来实现
使用Lock和Condition来实现
对比两种方式,Condition的方式可以更加精确的指定哪些线程被唤醒
public class MyContainer2<T> {
final private LinkedList<T> lists = new LinkedList<>();
final private int MAX = 10; //最多10个元素
private int count = 0;
private Lock lock = new ReentrantLock();
private Condition producer = lock.newCondition();
private Condition consumer = lock.newCondition();
public void put(T t) {
try {
lock.lock();
while(lists.size() == MAX) { //想想为什么用while而不是用if?
producer.await();
}
lists.add(t);
++count;
consumer.signalAll(); //通知消费者线程进行消费
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public T get() {
T t = null;
try {
lock.lock();
while(lists.size() == 0) {
consumer.await();
}
t = lists.removeFirst();
count --;
producer.signalAll(); //通知生产者进行生产
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return t;
}
public static void main(String[] args) {
MyContainer2<String> c = new MyContainer2<>();
//启动消费者线程
for(int i=0; i<10; i++) {
new Thread(()->{
for(int j=0; j<5; j++) System.out.println(c.get());
}, "c" + i).start();
}
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//启动生产者线程
for(int i=0; i<2; i++) {
new Thread(()->{
for(int j=0; j<25; j++) c.put(Thread.currentThread().getName() + " " + j);
}, "p" + i).start();
}
}
}
13,并发容器
hashtable,collections.synchtonizedxxx 并发不太高的场景下使用
//static Queue<String> tickets = new ConcurrentLinkedQueue<>();
并发量叫高的情况下使用
currentHashMap 不排序 currentHashSet
ConcurrentSkipListMap<>(); //高并发并且排序ConcurrentSkipListSet
queue 队列
Queue<String> strs = new ConcurrentLinkedQueue<>();
for(int i=0; i<10; i++) {
strs.offer("a" + i); //add
}
System.out.println(strs);
System.out.println(strs.size());
System.out.println(strs.poll()); 取后删除
System.out.println(strs.size());
System.out.println(strs.peek()); 只取不删除
System.out.println(strs.size());
无界队列 LinkedBlockingQueue
static BlockingQueue<String> strs = new LinkedBlockingQueue<>();
static Random r = new Random();
public static void main(String[] args) {
new Thread(() -> {
for (int i = 0; i < 100; i++) {
try {
strs.put("a" + i); //如果满了,就会等待
TimeUnit.MILLISECONDS.sleep(r.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "p1").start();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
for (;;) {
try {
System.out.println(Thread.currentThread().getName() + " take -" + strs.take()); //如果空了,就会等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "c" + i).start();
}
}
}
有界队列
static BlockingQueue<String> strs = new ArrayBlockingQueue<>(10);
===========================
static BlockingQueue<MyTask> tasks = new DelayQueue<>();
线程池
当洗个线程需要返回值时用callable 接口,runnable没有返回值
Executor
ExecutorService submit
Callable = Runnable
Executors
ThreadPool
Future 拿到未来的返回值,调用future时会阻塞,直到任务执行结束
fixed cached single scheduled workstealing forkjoin
ThreadpoolExecutor
PStreamAPI
Executors.newFixedThreadPool(5); 固定线程池
===========
ExecutorService service = Executors.newCachedThreadPool(); 可缓存线程池 没有空闲线程就新建一个线程,有空闲线程就复用。默认时间为60s,60秒后线程自动死亡
===========
ExecutorService service = Executors.newSingleThreadExecutor(); 单线程池
===========
ScheduledExecutorService service = Executors.newScheduledThreadPool(4); 周期线程 定时任务,好处:线程可以重复使用
service.scheduleAtFixedRate(()->{
try {
TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}, 0, 500, TimeUnit.MILLISECONDS);
=============
ExecutorService service = Executors.newWorkStealingPool(); 任务执行完自动找活干 工作窃取 CPU是几核默认启用几个线程,守护线程,实现方式是ForkJoinPool
=============
分叉合并 递归调用线程
public class T12_ForkJoinPool {
static int[] nums = new int[1000000];
static final int MAX_NUM = 50000;
static Random r = new Random();
static {
for(int i=0; i<nums.length; i++) {
nums[i] = r.nextInt(100);
}
System.out.println(Arrays.stream(nums).sum()); //stream api
}
/*
static class AddTask extends RecursiveAction {
int start, end;
AddTask(int s, int e) {
start = s;
end = e;
}
@Override
protected void compute() {
if(end-start <= MAX_NUM) {
long sum = 0L;
for(int i=start; i<end; i++) sum += nums[i];
System.out.println("from:" + start + " to:" + end + " = " + sum);
} else {
int middle = start + (end-start)/2;
AddTask subTask1 = new AddTask(start, middle);
AddTask subTask2 = new AddTask(middle, end);
subTask1.fork();
subTask2.fork();
}
}
}
*/
static class AddTask extends RecursiveTask<Long> {
private static final long serialVersionUID = 1L;
int start, end;
AddTask(int s, int e) {
start = s;
end = e;
}
@Override
protected Long compute() {
if(end-start <= MAX_NUM) {
long sum = 0L;
for(int i=start; i<end; i++) sum += nums[i];
return sum;
}
int middle = start + (end-start)/2;
AddTask subTask1 = new AddTask(start, middle);
AddTask subTask2 = new AddTask(middle, end);
subTask1.fork();
subTask2.fork();
return subTask1.join() + subTask2.join();
}
}
public static void main(String[] args) throws IOException {
ForkJoinPool fjp = new ForkJoinPool();
AddTask task = new AddTask(0, nums.length);
fjp.execute(task);
long result = task.join();
System.out.println(result);
//System.in.read();
}
}
=================
线程池核心类 ThreadPoolExecutor 可以实现自定义线程池
| service.scheduleAtFixedRate(()->{ |
| try { |
| TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000)); |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| System.out.println(Thread.currentThread().getName()); |
}, 0, 500, TimeUnit.MILLISECONDS);
|
ScheduledExecutorService service = Executors.newScheduledThreadPool(4);
|
线程池工作原理:
1,查看核心线程是否已满,如果没有创建新线程执行任务;
2,如果核心线程达到数量,则把任务丢进队列,如果队列队列没满,则等待被调用,如果队列已满则执行第三步
3,查看最大线程数是否已达到数量,如果没有则新建线程执行任务,如果达到最大线程则执行拒绝策略
线程池的拒绝策略有
1,丢掉任务,抛出异常
2,丢掉任务,不抛出异常
3,把队列里较早的任务移除,尝试丢进队列
4,把任务丢给主线程
阻塞队列有:
1,new ArrayBlockingQueue<>(5) 基于数组的先进先出队列 有界
2,new LinkedBlockingQueue<>();基于链表的先进先出队列
3,new Synchronous<>()无缓冲的等待队列,无界