* 高并发JUC
*
* 1 是什么
* 2 能干嘛
* 3 去哪下
* 4 怎么玩
*
* 1 JUC (java.util.concurrent) java高并发工具包;
java.util.concurrent
java.util.concurrent.atomic (原子性不可分割)
java.util.concurrent.locks
mysql(hash定位和hash算法;avl|二叉平衡树、B- B+树;数据库事务(保证一次提交的sql要么同时成功要么同时失败) ACID)、redis(事务的开启)、hbase、hive
* 2 什么是线程/什么是进程?
winword.exe QQ.exe 360.exe
* savePoint
* 3 并发 / 并行
*lambda Express
*1 使用lambda Express的前提,这个接口必须首先是一个函数式接口(一个方法)
*2 小口诀
* 拷贝小括号,写死右箭头,落地大括号
@FunctionalInterface //显式声明,表示只有一个方法; 隐式声明是不需写,jvm底层编译时会自动加上; interface Foo{ public int add(int x, int y); } /** * @auther zzyy *lambda Express *1 使用lambda Express的前提,这个接口必须首先是一个函数式接口(一个方法) *2 小口诀 * 拷贝小括号,写死右箭头,落地大括号 */ public class TestLambda { public static void main(String[] args) { Foo foo = ((x, y) -> { System.out.println("Hello World"); return x + y; }); System.out.println(foo.add(3, 5)); } }
@FunctionalInterface
public interface Runnable {
public abstract void run();}
多线程(请求多资源少)编程
卖票不牵扯线程之间的横向通信;池子里有就拿走
* 题目:三个售票员 卖出 30张票
如何编写企业级的多线程程序的模板+讨论
1) implements Runnable
2) 匿名内部类
3) Lambda Express
1 在高内聚低耦合的前提下,线程 操作(某一个具体的业务方法,实例方法) 资源类
// 资源类 = 变量+ 方法 class Ticket{ private int number = 30; private 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(); } } } public class MyThread { public static void main(String[] args) { Ticket ticket = new Ticket(); new Thread(() -> { for (int i = 1; i <= 30; i++) ticket.sale(); }, "a").start(); new Thread(() -> { for (int i = 1; i <= 30; i++) ticket.sale(); }, "b").start(); new Thread(() -> { for (int i = 1; i <= 30; i++) ticket.sale(); }, "c").start(); /* new Thread(new Runnable() { //接口可以new,匿名内部类,Runnable里边只有一个run方法; @Override public void run() { for (int i = 1; i <= 30; i++){ ticket.sale(); } } }, "a").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 1; i <= 30; i++){ ticket.sale(); } } }, "b").start(); new Thread(new Runnable() { @Override public void run() { for (int i=1; i <= 30; i++){ ticket.sale(); } } }, "c").start();*/ } }
8 lock synchronized static
class Phone { public static synchronized void sendEmail() throws Exception { try { TimeUnit.SECONDS.sleep( 3 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("*********sendEmail"); } public synchronized void sendSMS()throws Exception { System.out.println("*********sendSMS"); } public void sayHello() { System.out.println("*********sayHello"); } } /** * @auther zzyy * 8 lock * 1 标准访问,请问先打印邮件还是短信? 两个synchronized修饰的方法,一个对象去访问:先打印邮件再短信; * 2 邮件方法里面新增暂停4秒钟的方法,请问先打印邮件还是短信?
先邮件后短信; * 3 新增一个普通的sayHello实例方法,请问先打印邮件还是hello?
先hello,不是抢一个对象; 普通实例方法跟synchronized没有关系 * 4 有两部手机,请问先打印邮件还是短信? * 换成两个对象(两个this)后,不是同一把锁了(不会去抢资源),情况立刻变化;先打印短信(因为邮件sleep了),后邮件; * 5 两个静态同步方法,同一部手机,请问先打印邮件还是短信? 先邮件后短信 static锁的是当前对象的模版(类名.Class),大的Class(模板可有多个this对象),不是当前this对象;
一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁 static锁的是对象全局锁,锁的是那个模板--工厂,跟当前this对象锁是两把不同的锁; * 6 两个静态同步方法,2部手机,请问先打印邮件还是短信? 先邮件后短信 一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁 * 7 1个静态同步方法,1个普通同步方法,1部手机,请问先打印邮件还是短信? 先打印短信(不跟你抢),再邮件; * 8 1个静态同步方法,1个普通同步方法,2部手机,请问先打印邮件还是短信? * 先打印短信,再邮件 */ public class Lock8Demo05 { public static void main(String[] args) throws InterruptedException { Phone phone = new Phone(); Phone phone2 = new Phone(); new Thread(() -> { try { phone.sendEmail(); } catch (Exception e) { e.printStackTrace(); } },"A").start(); //保证A优先于B先启动 try { Thread.sleep( 100 ); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> { try { //phone.sendSMS(); //phone.sayHello(); phone2.sendSMS(); } catch (Exception e) { e.printStackTrace(); } },"B").start(); } }
* 1 一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了, * 其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法;(synchronized锁的是这个方法所在的整个资源类) * * 2 锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法 * * 3 加个普通方法(它不属资源类)后发现和同步锁无关 * * 4 换成两个对象后,不是同一把锁了,情况立刻变化。 5/6 Class 都换成静态同步方法后,情况又变化 * 所有的非静态同步方法用的都是同一把锁——实例对象本身, * * synchronized实现同步的基础:Java中的每一个对象都可以作为锁。 * 具体表现为以下3种形式。 * 对于普通同步方法,锁是当前实例对象。 * 对于同步方法块,锁是Synchonized括号里配置的对象。 * 对于静态同步方法,锁是当前类的Class对象。 * 当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。 * 也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁, * 可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁, * 所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。 * * 所有的静态同步方法用的也是同一把锁——类对象Class本身, * 这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。 * 但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁, * 而不管是同一个实例对象的静态同步方法之间, * 还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!
List
new的是数组;Object类型;初始值是空,add添加时马上扩至10个;扩容,第一次扩容至15,第二次是22(加上原值的一半,15+7);
怎么扩容?copyOf ;线程不安全(多线程情况下)的;线程不安全的案例代码:
public class NotSafeDemo { public static void main(String[] args) { Listlist = new ArrayList(); // new CopyOnWriteArrayList();//Collections.synchronizedList(new ArrayList<>());//new Vector<>(); for (int i = 1; i <= 30 ; i++) { new Thread(() -> { list.add(UUID.randomUUID().toString().substring(0, 8)); //ArrayList在add时没有加synchronize锁 System.out.println(list); }, String.valueOf(i)).start(); } } }
* 1 故障现象
java.util.ConcurrentModificationException 并发修改的异常; 高并发多线程
* 2 导致原因
* 3 解决方案
①new Vector<>(); 线程安全的保证了数据一致性但性能不高,初始值为10; add方法加了synchronized锁,导致并发性急剧下降;
② Collections.synchronizedList(new ArrayList<>()); Collections是辅助工具类,可把线程不安全的类变成整体线程安全的;
③ CopyOnWriteArrayList(); // java.util.concurrent.CopyOnWriteArrayList
* 写时复制 读写分析 CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加,而是先将当前容器Object[]进行Copy, 复制出一个新的容器Object[] newElements,然后新的容器Object[] newElements里添加元素,添加完元素之后, 再将原容器的引用指向新的容器 setArray(newElements);。这样做的好处是可以对CopyOnWrite容器进行并发的读, 而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器 public boolean add(E e){ final ReentrantLock lock = this.lock; lock.lock(); try{ Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); //在老版本的基础上扩容一个 newElements[len] = e; //写进去; setArray(newElements); //赋值回去 return true; } finally { lock.unlock(); //释放锁,下一个进来; } }
* 4 优化建议(同样的错误 ,不来第2次)
set、map同样也是线程不安全的,解决方法同理:
Setset = new CopyOnWriteArraySet<>(); //Collections.synchronizedSet(new HashSet<>()) //new HashSet<>() ;CopyOnWriteArraySet类底层用的还是调用new CopyOnWriteArrayList()
Mapmap = new ConcurrentHashMap<>(); //Collections.synchronizedMap(new HashMap<>()); //
HashSet底层就是HashMap(); default initial capacity (16) and load factor (0.75). 12扩容;HashSet add时是加一个,HashMap是加的k,v;
HashSet的add方法底层 public boolean add(E e) { return map.put(e, PRESENT)==null; } HashSet的add加一个e,底层其实是调的就是HashMap的put方法, HashSet add的那个e就是HashMap的key,HashMap最不重要的就是那个value, private static final Object PRESENT = new Object();
基本类型如int 传的就是个复印件,把int age = 20复印了传给了changeValue1,传时还是20,后来它改了为30;手上的原件是没变,它打印的是main里边的age;
person指向的引用是 abc,另一个指向的是xxx,把内存地址的值给改了;
str在常量池里边,abc,
消费者、生产者模型 多线程的交互
* 题目:现在两个线程,可以操作初始值为零的一个变量,
* 实现一个线程对该变量加1,一个线程对该变量减1,
* 实现交替,来10轮,变量初始值为零。
*
* 1 高内聚低耦合前提下,线程操作资源类
* 2 判断/干活/通知
* 3 多线程编程需要注意,防止多线程的虚假唤醒,多线程的判断不可以使用if,用while
A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup.
While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened,
and continuing to wait if the condition is not satisfied. In other words, waits should always occur in loops, like this one: synchronized (obj) { while () obj.wait(timeout); ... // Perform action appropriate to condition }
class Aircondition { private int number = 0; private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public void increment() throws Exception { lock.lock(); try { //1.判断 while (number != 0) { condition.await(); //this.wait(); } //2.干活 number++; System.out.println(Thread.currentThread().getName() + "\t" + number); //3.通知 condition.signalAll(); //this.notifyall(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void decrement() throws Exception { lock.lock(); try { while (number == 0) { condition.await(); } number--; System.out.println(Thread.currentThread().getName() + "\t" + number); condition.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } /* public synchronized void increment() throws Exception{ //1.判断 while (number != 0 ){ //不能用if, if是被唤醒之后还要重新判断;等的时间越长,线程优先级越高;while是要让它重新 唤醒之后还要重新判断 过次安检 this.wait(); } //2.干活 number++; System.out.println(Thread.currentThread().getName() + "\t" + number); //3.通知 this.notifyAll(); } public synchronized void decrement() throws Exception{ //1.判断 while (number ==0 ){ this.wait(); } //2.干活 number--; System.out.println(Thread.currentThread().getName() + "\t" + number); //3.通知 this.notifyAll(); }*/ } public class ProConsumer { public static void main(String[] args) { Aircondition aircondition = new Aircondition(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { Thread.sleep(200); aircondition.increment(); } catch (Exception e) { e.printStackTrace(); } } }, "A").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { Thread.sleep(300); aircondition.decrement(); } catch (Exception e) { e.printStackTrace(); } } }, "B").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { Thread.sleep(400); aircondition.increment(); } catch (Exception e) { e.printStackTrace(); } } }, "C").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { Thread.sleep(500); aircondition.decrement(); } catch (Exception e) { e.printStackTrace(); } } }, "D").start(); } }
* 备注:多线程之间按顺序调用,实现A->B->C 实现精准打击
* 三个线程启动,要求如下:
*
* AA打印5次,BB打印10次,CC打印15次
* 接着
* AA打印5次,BB打印10次,CC打印15次
* 来10轮
synchronized是系统的关键保留字 和 Lock只是一个普通的类;
private Lock lock = new ReentrantLock();
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition(); 一把锁,有3把备用的钥匙c1 c2 c3;
class SharData{ private int number = 1; private Lock lock = new ReentrantLock(); private Condition c1 = lock.newCondition(); private Condition c2 = lock.newCondition(); private Condition c3 = lock.newCondition(); public void print5(){ lock.lock(); try { //1.判断 while (number != 1){ c1.await(); } //2.干活 for (int i = 1; i <= 5; i++){ System.out.println(Thread.currentThread().getName() + "\t" + i); } //3.通知 number = 2;//修改标志物 c2.signal(); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } } public void print10(){ lock.lock(); try { //1.判断 while (number != 2){ c2.await(); } //2.干活 for (int i = 1; i <= 10; i++){ System.out.println(Thread.currentThread().getName() + "\t" + i); } //3.通知 number = 3; c3.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void print15(){ lock.lock(); try { //1.判断 while(number != 3){ c3.await(); } //2.干活 for (int i = 1; i <= 15; i++){ System.out.println(Thread.currentThread().getName() + "\t" + i); } //3.通知 number =1; c1.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } public class ConditionDemo { public static void main(String[] args) { SharData sharData = new SharData(); new Thread(() -> {for (int i = 1; i <= 10; i++){ sharData.print5(); }}, "a").start(); new Thread(() -> {for (int i = 1; i <= 10; i++){sharData.print10();}}, "b").start(); new Thread(() -> {for (int i = 1; i <= 10; i++){sharData.print15();}}, "c").start(); } }
使用Lock锁
public interface Lock Lock implementations provide more extensive locking operations than can be obtained using synchronized methods and statements. They allow more flexible structuring, may have quite different properties, and may support multiple associated Condition objects public interface Condition Condition factors out the Object monitor methods (wait, notify and notifyAll) into distinct objects to give the effect of having multiple wait-sets per object, by combining them with the use of arbitrary Lock implementations. Where a Lock replaces the use of synchronized methods and statements, a Condition replaces the use of the Object monitor methods.