我们实际上去公司写线程的代码时,不能让我们的资源类去继承Thread或者实现Runnable接口,我们应该将资源类完全隔离开来,它里面就只有属性和方法。
//基本的买票例子 //真正的多线程开发,公司中的开发一定要降低耦合性,线程就是一个单独的资源类,没有任何的附属操作 //里面包含有1.属性 2.方法 public class SaleTicketDemo01 { public static void main(String[] args) { //并发:多个线程操作同一个资源类 Ticket ticket = new Ticket(); 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(); } } //资源类 OOP编程 class Ticket{ //属性,方法 private int number = 50; //买票的方式 //synchronize本质就是一个队列 public synchronized void sale(){ if (number>0){ System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"张票,剩余"+number); } } }
我们通过运用Lock的实现类来加锁
我们先来看一下ReentrantLock的构造器,它默认是返回一个非公平锁,这让的目录就是提高运行效率,比如说
公平锁:十分公平,可以先来后到
非公平锁:十分不公平,可以插队
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; //基本的买票例子 //真正的多线程开发,公司中的开发一定要降低耦合性,线程就是一个单独的资源类,没有任何的附属操作 //里面包含有1.属性 2.方法 public class SaleTicketDemo02 { public static void main(String[] args) { //并发:多个线程操作同一个资源类 Ticket2 ticket = new Ticket2(); 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(); } } //资源类 OOP编程 //1.new ReentrantLock //2.加锁 //3.解锁 class Ticket2{ //属性,方法 private int number = 50; Lock lock = new ReentrantLock(); //买票的方式 //synchronize本质就是一个队列 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 A { public static void main(String[] args) { Data data = new Data(); new Thread(()->{ for (int i = 0; i < 20; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); new Thread(()->{ for (int i = 0; i < 20; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); } } //等待,业务,通知 class Data{//数字 资源类 private int number = 0; //+1 public synchronized void increment() throws InterruptedException { if (number!=0){ this.wait(); } number++; System.out.println(Thread.currentThread().getName()+"加操作"); this.notifyAll(); } //-1 public synchronized void decrement() throws InterruptedException { if (number==0){ this.wait(); } number--; System.out.println(Thread.currentThread().getName()+"减操作"); this.notifyAll(); } }
这里一定要用while,if只会判断一次,等待应该总是出现在循环中。不能说判断条件中途改变了就立刻不wait了
public class A { public static void main(String[] args) { Data data = new Data(); new Thread(()->{ for (int i = 0; i < 20; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); new Thread(()->{ for (int i = 0; i < 20; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); new Thread(()->{ for (int i = 0; i < 20; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"C").start(); new Thread(()->{ for (int i = 0; i < 20; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"D").start(); } } //等待,业务,通知 class Data{//数字 资源类 private int number = 0; //+1 public synchronized void increment() throws InterruptedException { while (number!=0){ this.wait(); } number++; System.out.println(Thread.currentThread().getName()+"加操作"); this.notifyAll(); } //-1 public synchronized void decrement() throws InterruptedException { while (number==0){ this.wait(); } number--; System.out.println(Thread.currentThread().getName()+"减操作"); this.notifyAll(); } }
condition就是替代同步监视,替换掉我们之前用的Synchronized的版本
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class B { public static void main(String[] args) { Data2 data = new Data2(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"C").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"D").start(); } } //等待,业务,通知 class Data2{//数字 资源类 private int number = 0; Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); //+1 public void increment() throws InterruptedException { lock.lock(); try { while (number!=0){ condition.await(); } number++; condition.signalAll(); System.out.println(Thread.currentThread().getName()+"加操作"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } //-1 public void decrement() throws InterruptedException { lock.lock(); try { while (number==0){ condition.await(); } number--; condition.signalAll(); System.out.println(Thread.currentThread().getName()+"减操作"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
condition除了可以替换wait和notifyall更重要的一点功能使它强于sychronized就是它可以精准的通知和唤醒线程,notify()只能随机唤醒一个线程,是由线程调度器随机分配的,notifyall它默认唤醒所有线程。
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; //A执行完调用B,B执行完调用C,C执行完调用A public class C { public static void main(String[] args) { Data3 data3 = new Data3(); new Thread(()->{ for (int i = 0; i < 10; i++) { data3.print1(); } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { data3.print2(); } },"B").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { data3.print3(); } },"C").start(); } } class Data3{//资源类 private int number = 1;//1A 2B 3C private Lock lock = new ReentrantLock(); //创建三个不同的监视器,通过监视器来判断我们到底应该唤醒的是哪个人 Condition condition1 = lock.newCondition(); Condition condition2 = lock.newCondition(); Condition condition3 = lock.newCondition(); public void print1(){ lock.lock(); try { //业务,判断是否等待的过程,判断是否执行,通知 while (number!=1){ //等待 condition1.await(); } System.out.println(Thread.currentThread().getName()+"在执行"); number = 2; condition2.signal(); } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } public void print2(){ lock.lock(); try { //业务,判断是否等待的过程,判断是否执行,通知 while (number!=2){ condition2.await(); } System.out.println(Thread.currentThread().getName()+"在执行"); number = 3; condition3.signal(); } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } public void print3(){ lock.lock(); try { //业务,判断是否等待的过程,判断是否执行,通知 while (number!=3){ condition3.await(); } System.out.println(Thread.currentThread().getName()+"在执行"); number = 1; condition1.signal(); } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } }
这种精准调用的应用:生产线:下单--》支付--》交易--》物流
锁的是谁?
import java.util.concurrent.TimeUnit; /** * 标准情况下,是先打印发短信还是先打印打电话呢? * 那我们要是在这个sendSms中去加一个sleep呢,结果又会怎样呢? * 其实这里无论怎么变化都是先会打印发短信,再打印打电话的。 */ public class Test1 { 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{ //synchronized其实锁的是方法的调用者,这也就不难解释上面我们只创建了一个Phone对象 public synchronized void sendSms() { System.out.println("发短信"); } public synchronized void call() { System.out.println("打电话"); } }
import java.util.concurrent.TimeUnit; public class Test2 { public static void main(String[] args) { Phone2 phone = new Phone2(); new Thread(()->{ phone.sendSms(); },"A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone.hello(); },"B").start(); } } class Phone2{ 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.println("hello"); } }
import java.util.concurrent.TimeUnit; public class Test3 { public static void main(String[] args) { Phone3 phone1 = new Phone3(); Phone3 phone2 = new Phone3(); new Thread(()->{ phone1.sendSms(); },"A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone2.call(); },"B").start(); } } class Phone3{ //static 静态方法 // 类一加载了就有了,加了static的同步方法锁的就是方法调用者的class // 当然我们也知道无论有多少个对象都只有一个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("打电话"); } }
import java.util.concurrent.TimeUnit; /** * 锁的东西都不一样 */ public class Test4 { public static void main(String[] args) { Phone4 phone = new Phone4(); new Thread(()->{ phone.sendSms(); },"A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone.call(); },"B").start(); } } class Phone4 { //静态同步方法 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("打电话"); } }
首先Vector是线程安全的,ArrayList是线程不安全的
import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput; import java.lang.reflect.Array; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; // java.util.ConcurrentModificationException 并发修改异常 public class ListTest { public static void main(String[] args) { /** * 原本:List<String> list = new ArrayList<>(); * 解决方法: * 1.将ArrayList换成Vector List<String> list = new Vector<>(); * 2.采用集合的顶级工具类来进行一个转化 List<String> list = Collections.synchronizedList(new ArrayList<>()); * 3.采用JUC包下的 CopyOnWrite:写入时复制(提高计算机程序设计领域的一种优化策略)List<String> list = new CopyOnWriteArrayList<>(); * 为什么了要有这个东西呢,因为在多线程写入的时候可能出现写入覆盖的问题,在写入的时候呢我们先copy一份再写入再放回去。 * 读写分离 (写入的时候复制一份来写) * CopyOnWriteArrayList 比 Vector 牛逼在那里,只要有synchronized方法效率就比较低,Vector用的是synchornized * CopyOnWriteArrayList 用的是Lock */ List<String> list = new CopyOnWriteArrayList<>(); for (int i = 0; i < 10; i++) { new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(list); },String.valueOf(i)).start(); } } }
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) { Set<String> set1 = new HashSet<>(); //方法一 Set<String> set2 = Collections.synchronizedSet(new HashSet<>()); //方法二 Set<String> set3 = new CopyOnWriteArraySet<>(); for (int i = 0; i < 100; i++) { new Thread(()->{ set.add(UUID.randomUUID().toString().substring(0,5)); },String.valueOf(i)).start(); } } }
扩展:HashSet的底层就是一个HashMap,就是用了它的这个key,key是不重复的。
HashSet.add(),其实就是将你要存的数据当作HashMap的key存进去罢了。key不会重复,set不允许有重复的值。
PRESENT其实就是一个固定的不变的值。
HashMap构造方法中:
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) { //map是这样用的吗? 不是,工作中不用这样的HashMap // 默认等价于什么? Map<String, Object> map = new HashMap<>(16,0.75); Map<String, Object> map = new HashMap<>(); //方法一: Map<Object, Object> objectObjectMap = Collections.synchronizedMap(new HashMap<>()); //方法二: Map<String,Object> map1 = new ConcurrentHashMap<>(); //加载因子,初始容量 for (int i = 0; i < 30; i++) { new Thread(()->{ map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5)); },String.valueOf(i)).start(); } } }
原文:https://www.cnblogs.com/yaoyaoo/p/14375247.html