synchronized锁的对象是方法的调用者,即对象本身,当两个同步方法被调用时,谁先拿到锁先执行。
代码示例 public class OrderTest { public static void main(String[] args) { Phone phone = new Phone(); new Thread(() -> { phone.sendSMS(); }).start(); new Thread(() -> { phone.call(); }).start(); } } class Phone { public synchronized void call() { System.out.println("call"); } public synchronized void sendSMS() { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sendSMS"); } /** * 输出:等待3秒后才输出,虽然同时开启了2个线程调用不同方法,但是因为都是调用了phone对象的同步方法, * 争抢同一把锁,会有执行的先后顺序 * sendSMS * call */ }同步方法与普通方法被同时调用,普通方法是不会被同步方法锁阻塞的,会直接执行。
两个对象,两个同步方法被同时调用时,对应两把锁,不会互相争抢锁,各自直接执行。
被static修饰的静态同步方法,锁的是Class,跟创建的对象无关,只有Class这一把锁。
一个是被static修饰的静态同步方法,一个是普通同步方法,因为两个的锁不一样,不会争抢锁,static同步方法锁的是Class,而普通同步方法锁的是调用者对象。
线程安全集合类 CopyOnWriteArrayList public class ListTest { public static void main(String[] args) { //多线程下ArrayList边遍历边修改增加会报ConcurrentModificationException // List<String> list = new ArrayList<>(); // Vector 是同步集合但效率不高 // List<String> list = new Vector<>(); // 集合工具类同步 // List<String> list = Collections.synchronizedList(new ArrayList<>()); //JUC的读写分离集合类,写入时复制 List<String> list = new CopyOnWriteArrayList<>(); for (int i = 0; i < 10; i++) { new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0,2)); System.out.println(list); }).start(); } } } CopyOnWriteArraySet public class SetTest { public static void main(String[] args) { /** * HashSet本质上是HashMap * 利于了HashMap的KEY不重复的功能实现元素唯一性,值是固定的PRESENT * private static final Object PRESENT = new Object(); */ //多线程下HashSet边遍历边修改增加会报ConcurrentModificationException // Set<String> set = new HashSet<>(); // 集合工具类同步 // Set<String> set = Collections.synchronizedSet(new HashSet<>()); //JUC的读写分离集合类,写入时复制 Set<String> set = new CopyOnWriteArraySet<>(); for (int i = 0; i < 10; i++) { new Thread(() -> { set.add(UUID.randomUUID().toString().substring(0, 2)); System.out.println(set); }).start(); } } } ConcurrentHashMap原理
BlockingQueue阻塞队列,指的是当生产者往队列写入生产或者消费者从队列中获取消费时,如果发现队列是满的或者是空的,则写入或者取出的操作将阻塞着,直到有队列有空间继续写入或者有元素可以取出消费。
写入:如果队列满了,就必须阻塞等待消费
取出:如果队列是空的,就必须阻塞等待生产
阻塞队列使用场景:多线程并发处理,线程池
四组写入取出API抛出异常
不会抛出异常,有返回值
阻塞等待
超时等待
方式 抛出异常 不会抛出异常,有返回值 阻塞等待 超时等待添加 add() offer() put() offer(E e, long t, TimeUnit t)
移除 remove() poll() take() poll(long timeout, TimeUnit unit)
检查队首元素 element() peek() - -
代码示例 public class BlockingQueueTest { public static void main(String[] args) throws InterruptedException { test4(); } /** * 抛出异常API */ public static void test1(){ ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3); //写入成功返回true,内部其实是调用offer(e)方法 System.out.println(queue.add("one")); System.out.println(queue.add("two")); System.out.println(queue.add("three")); //IllegalStateException: Queue full //当队列满时继续写入会抛IllegalStateException: Queue full 异常 // System.out.println(queue.add("four")); //检查队首元素,有值返回元素但不移除元素,内部其实是调用 peek()方法 //如果队列为空检查不到队首元素则抛出NoSuchElementException 异常 System.out.println(queue.element()); System.out.println("--------------------------------------"); //取出成功返回元素,内部其实是调用poll()方法 System.out.println(queue.remove()); System.out.println(queue.remove()); System.out.println(queue.remove()); //NoSuchElementException //当队列为空时继续取出会抛NoSuchElementException 异常 // System.out.println(queue.remove()); } /** * 输出: * true * true * true * one * -------------------------------------- * one * two * three */ /** * 不会抛出异常,有返回值 API */ public static void test2(){ ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3); //写入成功返回true System.out.println(queue.offer("one")); System.out.println(queue.offer("two")); System.out.println(queue.offer("three")); //当队列满时继续写入会返回false System.out.println(queue.offer("four")); //检查队首元素,有值返回元素但不移除元素 //如果队列为空检查不到队首元素则返回null 空 System.out.println(queue.peek()); System.out.println("--------------------------------------"); //取出成功返回元素 System.out.println(queue.poll()); System.out.println(queue.poll()); System.out.println(queue.poll()); //当队列为空时继续取出会则返回null 空 System.out.println(queue.poll());//null System.out.println(queue.peek());//null } /** * 输出: * true * true * true * false * one * -------------------------------------- * one * two * three * null * null */ /** * 阻塞等待 API ,因为阻塞等待所以不需要检查队首元素 */ public static void test3() throws InterruptedException { ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3); //写入成功返回true queue.put("one"); queue.put("two"); queue.put("three"); //当队列满时会阻塞等待,直到队列有空闲空间可以继续写入 // queue.put("four"); System.out.println("--------------------------------------"); //取出成功返回元素 System.out.println(queue.take()); System.out.println(queue.take()); System.out.println(queue.take()); //当队列为空时继续取出会阻塞等待,直到队列有元素时继续取出 // System.out.println(queue.take()); } /** * -------------------------------------- * one * two * three */ /** * 超时等待 API */ public static void test4() throws InterruptedException { ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3); //写入成功返回true System.out.println(queue.offer("one",2, TimeUnit.SECONDS)); System.out.println(queue.offer("two",2, TimeUnit.SECONDS)); System.out.println(queue.offer("three",2, TimeUnit.SECONDS)); //当队列满时继续写入会返回进行超时等待,如果在指定时间后写入不成功返回false System.out.println(queue.offer("four",2, TimeUnit.SECONDS)); System.out.println("--------------------------------------"); //取出成功返回元素 System.out.println(queue.poll(2, TimeUnit.SECONDS)); System.out.println(queue.poll(2, TimeUnit.SECONDS)); System.out.println(queue.poll(2, TimeUnit.SECONDS)); //当队列为空时继续取出会进行超时等待,如果在指定时间后取出不成功返回null System.out.println(queue.poll(2, TimeUnit.SECONDS));//null } /**输出: * true * true * true * false * -------------------------------------- * one * two * three * null */ } SynchronousQueue
SynchronousQueue不存储元素,put写入一个元素后,必须从里面先take取出元素,否则无法再继续put写入元素。
//示例不好待补充
常用辅助类 CountDownLatch