文章目录

  • java核心技术 基础知识<集合&并发part1>
    • 9 泛型程序设计
      • 9.5 算法
      • 9.6 遗留的集合
    • 14 并发
      • 14.2 中断线程
      • 14.3 线程状态
      • 14.4 线程属性
      • 14.5 同步
        • 14.5.3 锁对象
        • 14.5.4 条件对象
        • 14.5.5 synchronized 关键字
        • 14.5.6 同步阻塞
        • 14.5.7 监视器概念
        • 14.5.8 Volatile 域
        • 14.5.9 final 变量
        • 14.5.10 原子性
        • 14.5.11 死锁
        • 14.5.12 线程局部变量
        • 14.5.13 锁测试与超时
        • 14.5.14 读/ 写锁
      • 14.6 阻塞队列

java核心技术 基础知识<集合&并发part1>

9 泛型程序设计

9.5 算法

排序和混排:

java.util.Collections
static <T extends Comparable<? super T>> void sort( List<T> elements);
// 归并排序
static void shuffle(List<?> elements)
static void shuffle(List<?> elements, Random r) // 随即打乱java.util.List<E>
default void sort(Comparator<? super T> comparator) // list的排序java.util.Comparator<T>
static <T extends Comparable<? super T>> Comparator<T> reverseOrder()
default Comparator<T> reversed( ) // 比较器逆序

二分搜索:

这个方法将返回这个键在列表中的索引, 如果在列表中不存在这个键将返回负值i。 在这种情况下,应该将这个键插人到列表索引 -i-1 的位置上, 以保持列表的有序性。

static <T extends Comparable<? super T>> int binarySearch( List<T>elements , T key)
static <T> int binarySearch(List<T> elements, T key, Conparator<? super T> c)// 从有序列表中搜索一个键, 如果元素扩展了AbstractSequentialList 类,
// 则采用线性查找,否则将采用二分查找
i = Collections.binarySearch(c, element) ;
i = Collections.binarySearch(c, element, comparator);

其他简单算法:

使用简单算法的好处,读者可以很轻松了解作者的意图。例如:

for (int i = 0; i < words.size(); i++)if (words.get(i).equals("C++", j) words.set(i , "Java") ;
// 可以用如下简单的一句话表达
Collections.repiaceAll ("C++", "Java") ;words.removeIf( w -> w.length() <= 3) ; // 删除短于3长度的单词
words.replaceAll(String::toLowerCase) ; // 把单词转小写

Collections:
min
max
copy
fill
addAll
replaceAll
indexOfSubList
lastIndexOfSubList
swap
reverse
rotate
frequency
disjoint

Collection:
removeIf

批操作:
removeAll,删除出现的
retainAll,删除未出现的

集合和数组的转换:

集合to数组:

Object[]  values = staff.toArray(); // yes
String[] values = (String[]) staff.toArray(); // Error!
String[] values = staff.toArray(new String[0]); // yes
staff.toArray(new String[staff.size()]); // yes

数组to集合:
Arrays.asList包装器。

HashSet<String> staff = new HashSet<>(Arrays.asList(values));

编写自己的算法:
如果编写自己的算法(实际上, 是以集合作为参数的任何方法),应该尽可能地使用接口而不要使用具体的实现

如果编写了一个返回集合的方法,可能还想要一个返回接口而不是返回类的方法, 因为这样做可以在日后改变想法,并用另一个集合重新实现这个方法。

9.6 遗留的集合

Hashtable
与HashMap作用一样,接口一样。
与Vector类的方法一样,Hashtable也是同步的。如果对同步性或者遗留代码兼容性没有要求,就应该使用HashMap。如果需要并发,使用ConcurrentHashMap

枚举:
遗留集合用Enumeration接口对元素进行遍历。和Iterator很类似。
hasMoreElement, nextElement

C++注释:C++中迭代器作参数很普遍。Java中很少,因为传递集合比传递迭代器更明智。

java.util.Enumeration<E>
java.util.Hashtable<E>
java.util.Vector<E>

属性映射:

java.util.Properties

属性映射(property map) 是一个类型非常特殊的映射结构。它有下面3 个特性:
• 键与值都是字符串。
• 表可以保存到一个文件中, 也可以从文件中加载。
• 使用一个默认的辅助表。

栈:

java.util.Stack<E>

位集:
Java 平台的BitSet 类用于存放一个位序列(它不是数学上的集, 称为位向量或位数组更为合适)。如果需要高效地存储位序列(例如,标志)就可以使用位集。由于位集将位包装在字节里, 所以,使用位集要比使用Boolean 对象的ArrayList 更加高效

BitSet 类提供了一个便于读取、设置或清除各个位的接口。使用这个接口可以避免屏蔽和其他麻烦的位操作

java.util.BitSet
bucketOfBits.get(i)
bucketOfBits.set(i)
bucketOfBits.clear(i)

14 并发

资料收集:
java主线程结束和子线程结束之间的关系
Java 并发编程:核心理论
Java & Go 并发编程系列

java和golang对比:

java:  java.util.concurrent.CountDownLatch
golang: sync.WaitGroup

如何实现并发:
1 ) 将任务代码移到实现了Runnable 接口的类的run 方法中。这个接口非常简单, 只有一个方法:

public interface Runnable
{void run();
}

由于只有一个方法的接口又称为函数式接口,可以用lambda表达式:

Runnable r = () -> { task code };

2 ) 由Runnable 创建一个Thread 对象:

Thread t = new Thread(r);

3 ) 启动线程:

t.start();

注:
发生InterruptedException异常时,run方法将结束执行。
也可以extends Thread来构造子类对象,调用run,不过不推荐,应该将要并行运行的任务运行机制解耦合。
不要用run方法,直接调run,只会执行同一个线程中的任务,Thread.start会创建一个执行run的新线程。

第三种方式是通过 Callable 和 Future 创建线程。

java.lang.Thread
Thread(Runnable target)
//构造一个新线程, 用于调用给定目标的run() 方法。void start( )
// 启动这个线程, 将引发调用run() 方法。这个方法将立即返回, 并且新线程将并发运行。void run( )
// 调用关联Runnable的run方法。java.lang.Runnable
void run( )
// 必须覆盖这个方法, 并在这个方法中提供所要执行的任务指令。

14.2 中断线程

线程终止于:

  1. run方法的最后一条语句执行完后,return返回时
  2. 出现方法中未捕获的异常
  3. stop方法(启用)

没有强制线程终止的方法,interrupt可以用来请求终止线程。
线程的中断状态将被置位。boolean标志,每个线程应该不时检查这个标记,以判断线程是否被中断。

线程主动获取中断状态
要想弄清中断状态是否被置位, 首先调用静态的Thread.currentThread 方法获得当前线程, 然后调用islnterrupted方法:

while (!Thread.currentThread().isInterrupted() && more work to do)
{do more work
}

线程被阻塞时(sleep或wait),被动调interrupt方法时,阻塞调用将会被Interrupted Exception打断。

delay时interrupt了,会抛出InterruptedException

示例:将中断作为一个终止的请求,循环判断isInterrupted

Runnable r = () -> {try{while (!Thread.currentThread() .isInterrupted() && more work to do){do more work}}catch(InterruptedException e){// thread was interr叩ted during sleep or wait}finally{// cleanup, if required}// exiting the run method terminates the thread
};

示例2:sleep时被打断,捕获InterruptedException。

Runnable r = () -> {try{while { more work to do){do more workThread.sleep(delay);}}catch(InterruptedException e){// thread was interrupted during sleep}finally{// cleanup, if required}// exiting the run method terminates the thread
};

底层的InterruptedException如何处理?

  1. 不要抑制在底层
  2. Thread.currentThread().interrupt()来设置中断状态,调用者检测。
  3. 更好选择 用throws InterruptedException标记方法,不采用try语句,让调用者捕获异常。

API:

java.lang.Thread
void interrupt()static boolean isInterrupted()
// 正在执行这一命令的线程是否中断?副作用:当前中断标志置位falseboolean isInterrupted() // 测试线程是否被终止,不改变中断状态
static Thread currentThread() // 当前线程对象

14.3 线程状态

6种,New(新创建),Runnable(可运行),Blocked(被阻塞),Waiting(等待),Timed waiting(计时等待),Terminated(被终止)。

New:
new Thread(r)之后,线程还未运行,一些基础工作。

Runnable:
调用start方法后,处于Runnable,操作系统给时间片就是运行,不给就不运行。
大多抢占时调度,或者yield、阻塞再让出线程。多处理器的及其可以有多个线程并行。

Blocked & Waiting & Timed waiting:
当获得内部的对象锁(不是java.util.concurrent)失败时,进入阻塞状态,直到其他线程释放锁,并且调度器允许本线程持有它时。

当线程等待另一个线程通知调度器一个条件时,它自己进入等待状态。Object.wait,或者Thread.join,或是java.util.concurrent库中的Lock或Condition时,就会出现这种情况。

有几个方法有一个超时参数。调用它们导致线程进人计时等待( timed waiting ) 状态。这一状态将一直保持到超时期满或者接收到适当的通知。

Terminated:
run自然退出或者未捕获的异常

API:

java.lang.Thread
void join()
void join(long millis)
Thread.State getState()
void stop()
void suspend()
void resume()

14.4 线程属性

优先级:
不要将程序构建为功能的正确性依赖于优先级。
API:

void setPriority(int newPriority) // 1-10
static void yield()
// 导致当前执行线程处于让步状态。如果有其他的可运行线程具有至少与此线程同样高
// 的优先级,那么这些线程接下来会被调度。注意,这是一个静态方法。

守护线程:
守护线程的唯一用途是为其他线程提供服务。当只剩下守护线程时, 虚拟机就退出了。
守护线程应该永远不去访问固有资源, 如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。

void setDaemon( boolean isDaemon )

未捕获异常处理器:
run语句抛出的非受查异常的处理器。
可以用setUncaughtExceptionHandler 方法为任何线程安装一个处理器。也可以用Thread类的静态方法setDefaultUncaughtExceptionHandler 为所有线程安装一个默认的处理器。

下图:fromJava:并发不易,先学会用

14.5 同步

解决同时对共享数据的存取问题。

javap -c -v XXX可以查看反编译,根据字节码分析并发会产生的问题。

两种机制防止代码块受并发访问干扰,一个synchronized关键字,一个ReentrantLock类。

14.5.3 锁对象

使用ReentrantLock保护代码的结构:

myLock.lock(); // a ReentrantLock object
try
{critical section // 临界区段
}
finally
{myLock.unlock(); // make sure the lock is unlocked even if an exception is thrown
}

Java锁–Lock实现原理(底层实现)

Interface Lock
实现Lock接口类:
ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock

锁是可重入的,使用持有计数(hold count)来跟踪对lock方法的嵌套调用。

java.util.concurrent.locks.Lock // interface
void lock( )
void unlock( )java,util.concurrent.locks.ReentrantLock // implement
ReentrantLock( )
ReentrantLock(boolean fair ) // 公平策略,慢

14.5.4 条件对象

使用一个条件对象(conditional variable)来管理那些已经获得了一个锁但是却不能做有用工作的线程。

一个锁对象可以有一个或多个相关的条件对象。你可以用newCondition方法获得一个条件对象。习惯上给每一个条件对象命名为可以反映它所表达的条件的名字

class Bank
{private Condition sufficientFunds;public Bank(){// bankLock新建一个条件变量sufficientFunds = bankLock.newCondition();}
}

注:

  1. 调await()会放弃锁,希望另一个线程signalAlll(),自己没有办法重新激活自身。专业叫法:进入该条件的等待集。
  2. 锁可用时,不会马上解除阻塞(与Lock不同),直到另一个线程调用同一条件的signalAll()为止。
  3. signal()会随机选一个,有死锁风险。
java.util.concurrent.locks.Lock
Condition newCondition( )
// 返回一个与该锁相关的条件对象。java.util.concurrent.locks.Condition
void await( )
// 将该线程放到条件的等待集中。
void signalAll( )
// 解除该条件的等待集中的所有线程的阻塞状态。
void signal( )
// 从该条件的等待集中随机地选择一个线程, 解除其阻塞状态。

总结一下
有关锁和条件的关键之处:
• 锁用来保护代码片段, 任何时刻只能有一个线程执行被保护的代码。
• 锁可以管理试图进入被保护代码段的线程。
• 锁可以拥有一个或多个相关的条件对象。
• 每个条件对象管理那些已经进入被保护的代码段但还不能运行的线程。

14.5.5 synchronized 关键字

每一个对象都有一个内部锁(对象锁)。如果一个方法用synchronized 关键字声明,那么对象的锁将保护整个方法。也就是说,要调用该方法, 线程必须获得内部的对象锁。
如果当前线程不是对象锁的持有者(和reentrantLock不同之处),会抛出IllegalMonitorStateException

java.lang.Object
void notifyAll()
// 解除同对象调wait的阻塞状态,只能在synchronized方法块中调用
// 如果当前线程不是对象锁的持有者,会抛出IllegalMonitorStateException
void notify()
// 随机选一个void wait()
// 等待直到被通知。如果当前线程不是对象锁的持有者,会抛出IllegalMonitorStateException
void wait(long millis)
void wait(long millis, int nanos)
// 等待直到被通知。
// 或者经过指定的时间。

等价代码:

public synchronized void method()
{method body
}
===============等价============
public void method()
{this.intrinsicLock.lock();try{method body}finally { this.intrinsicLock.unlock(); }
}

14.5.6 同步阻塞

sychronized应用于一个对象,同步代码块。

  1. 上一节介绍调用synchronized方法获得锁。
  2. 应用于对象,加锁代码块
synchronized (obj) // this is the syntax for a synchronized block
{critical section
}

有时程序员使用一个对象的锁来实现额外的原子操作, 实际上称为客户端锁定( client side locking) 。相比于用synchronized方法。

客户端锁定是非常脆弱的,通常不推荐使用。因为依赖于代码块里面(critical section里面)依然的不会有竞争条件的。

14.5.7 监视器概念

如果一个方法用synchronized 关键字声明,那么,它表现的就像是一个监视器方法。通过调用 wait / notifyAll / notify 来访问条件变量。

14.5.8 Volatile 域

Java并发编程:volatile关键字解析

Java内存模型规定所有的变量都是存在主存当中(类似于前面说的物理内存),每个线程都有自己的工作内存(类似于前面的高速缓存)。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。

当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:

  1. 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
  2. 禁止进行指令重排序。

14.5.9 final 变量

final声明域时,其他线程会在构造函数完成构造之后才看到这个final修饰的accounts 变量。
如果不使用final,就不能保证其他线程看到的是accounts 更新后的值,它们可能都只是看到null , 而不是新构造的HashMap。

14.5.10 原子性

高效的机器级指令保证操作的原子性。
如果有大量线程要访问相同的原子值, 性能会大幅下降, 因为乐观更新需要太多次重试。

java.util.concurrent.atomic
// 类
AtomicInteger
AtomicIntegerArray
AtomicIntegerFieldUpdater
AtomicLongLongAdder
// 可以有多个线程更新不同的加数,线程个数增加时会自动提供新的加数。
LongAccumulator// 方法
incrementAndGet()
decrementAndGet()
compareAndSet(oldV, newV) // CAS
updateAndGet() // 返回新值
accumulateAndGet() // 返回新值
getAndUpdate() // 返回原值
getAndAccumulate() // 返回原值

14.5.11 死锁

当程序挂起时, 键入CTRL+\, 将得到一个所有线程的列表。每一个线程有一个栈踪迹, 告诉你线程被阻塞的位置。

14.5.12 线程局部变量

避免共享变量的方式:使用ThreadLocal辅助类为各个线程提供各自的实例。

java.lang.ThreadLocal<T>
T get()
// 得到这个线程的当前值。如果是首次调用get, 会调用initialize 来得到这个值。
protected initialize( )
// 应覆盖这个方法来提供一个初始值。默认情况下,这个方法返回null
void set(T t)
void remove( )
static <S> ThreadLocal<S> withInitial (Supplier<? extends S> supplier)
// 创建一个线程局部变量,其初始值通过调用给定的supplier 生成。// 使用方式
public static final ThreadLocal<SimpleDateFormat> dateFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
// 访问
dateFormat.get().format(new Date())

14.5.13 锁测试与超时

尝试加锁,不成功立即离开去做其他事。
多路复用(类似golang select的default分支,不会阻塞)。带上timeout的类似 golang中<- time.After(xxx)分支。

如果调用带有用超时参数的tryLock, 那么如果线程在等待期间被中断,将抛出InterruptedException 异常。这是一个非常有用的特性,因为允许程序打破死锁

java.util.concurrent.locks.Lock
boolean tryLock()
// 尝试获得锁而没有发生阻塞;
boolean tryLock(long time, TimeUnit unit)
// 尝试获得锁,阻塞时间不会超过给定的值void lockInterruptibly()
// 它就相当于一个超时设为无限的tryLock 方法java.util.concurrent.locks.Condition
boolean await(long time , TimeUnit unit )
// 带超时的await
void awaitUninterruptibly()
//

14.5.14 读/ 写锁

和其他语言一样,同一资源,读可以同时,但是写行为和其他互斥。
ReentrantReadWriteLock就是读写锁。可分别获得读锁、写锁。

java.util.concurrent.locks.ReentrantReadWriteLock
Lock readLock( )
// 得到一个可以被多个读操作共用的读锁, 但会排斥所有写操作。
Lock writeLock( )
// 得到一个写锁, 排斥所有其他的读操作和写操作。

14.6 阻塞队列

并发程序的高级特性。较高层次,更方便、更安全。远离前面介绍的底层结构。

在协调多个线程之间的合作时, 阻塞队列是一个有用的工具。类似于golang的channel。
【Java&Go并发编程系列】1.阻塞队列与通道——BlockingQueue VS channel

包前缀:java.util.concurrent.XXX
ArrayBlockingQueue<E> // 循环数组实现LinkedBlockingQueue<E> // 链表实现
LinkedBlockingDeque<E> // 链表实现双端DelayQueue<E extends Delayed> // 延迟队列
// 只有那些延迟已经超过时间的元素可以从队列中移出。
Delayed
PriorityBlockingQueue<E> // 按优先级BlockingQueue<E> // interface
// implement by ArrayBlockingQueue, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, LinkedTransferQueue, PriorityBlockingQueue, SynchronousQueue
// 带时间限制的offer和pollBlockingDeque<E>
// implement by LinkedBlockingDequeTransferQueue<E>
// implement by LinkedTransferQueue
void transfer(E element)
boolean tryTransfer(E element, long time, TimeUnit unit)

add element remove 增、取、删并取,异常抛出IllegalStateException或则NoSuchElementException
offer peek poll 增、取、删并取,异常返回boolean或null
put take 增、取, 取不到阻塞

注释: poll 和peek 方法返回空来指示失败。因此,向这些队列中插入null 值是非法的。

JavaSE 7 增加了一个TransferQueue接口,允许生产者线程等待, 直到消费者准备就绪可以接收一个元素。(golang 阻塞发送者)

java核心技术 基础知识<集合并发part1>相关推荐

  1. Java核心技术基础知识学习之Java集合(三)

    文章目录 七.Java集合 7.6 Java 8 增强的 Map 集合 7.6.1 Java 8 中 Map 新增的方法 7.6.2 Java 8 改进的 HashMap 和 HashTable 实现 ...

  2. Java核心技术基础知识学习之面向对象(下)

    文章目录 五.面向对象(下) 5.1 包装类 5.2 处理对象 5.2.1 打印对象 5.2.2 ==和equals方法 5.3 类成员 5.3.1 理解类成员 5.3.2 单例类 5.4 final ...

  3. 《Java核心技术 ---- 基础知识》 ---- 第三章 java的基本程序设计结构

    一. java命名规则 类名 - 以大写字母开头的名词(骆驼命名法) 变量名 二.注释 行内注释 // 文本注释 /* */ 文本块注释 /** */ 三.数据类型 共8种基本类型,4种整型.2种浮点 ...

  4. Java面试基础知识III

    Java面试基础知识: 1.C++或Java中的异常处理机制的简单原理和应用. 当JAVA 程序违反了JAVA的语义规则时,JAVA虚拟机就会将发生的错误表示为一个异常.违反语义规则包括2种情况.一种 ...

  5. Java SE 基础知识

    Java SE 基础知识 1 2 @(Notes)[J2SE, Notes] VICTORY LOVES PREPARATION. 特别说明: 该文档在马克飞象查阅最佳: 本部分知识还在迭代中,欢迎补 ...

  6. Java进阶基础知识

    Java进阶基础知识 1.Java 基础 Java类设计的原则就是内聚性,一致性和封装性是Java设计的基本原则 1.1 Java基础理论 Java基础理论知识 1.2继承的优缺点 1.2.1优点 : ...

  7. 你觉得什么才是 Java 的基础知识?

    近日里,很多人邀请我回答各种j2ee开发的初级问题,我无一都强调java初学者要先扎实自己的基础知识,那什么才是java的基础知识?又怎么样才算掌握了java的基础知识呢?这个问题还真值得仔细思考. ...

  8. hashcode是什么意思_什么才是 Java 的基础知识?

    作者:晓风轻 链接:zhuanlan.zhihu.com/p/28615617 近日里,很多人邀请我回答各种j2ee开发的初级问题,我无一都强调java初学者要先扎实自己的基础知识,那什么才是java ...

  9. java类与接口思维导图_详解java接口基础知识附思维导图

    接口: 官方的含义是---->java接口是一系列方法的声明,是一些方法特征的集合 疑问: 那为什么不用抽象类呢?把他们共有的方法集合起来放在一个抽象类里面,同样可以调用哇,但是反过来想一想如果 ...

最新文章

  1. 64 任务调度神器 Airflow
  2. 合并分支到master_我敢打赌!这是全网最全的 Git 分支开发规范手册
  3. 产品中的实名认证该怎么设计?
  4. 史密斯圆图串并联口诀_看得懂的史密斯圆图(个人总结)
  5. css文字下滑,CSS3 文本下落渐变动效
  6. 系统架构师学习笔记-信息系统基础知识
  7. 一起来读you don't know javascript(一)
  8. 面向对象,局部变量和成员变量
  9. linux 运行魂斗罗,如何在linux/freebsd下玩模拟器游戏_linux教程
  10. python求矩阵的秩_python – 计算所有子矩阵有多少个矩阵具有满秩
  11. 用C#编写HMI界面,用USB转485和PLC通讯,PLC用的是台达的DVP-12SE系列
  12. 易语言注册机接码平台对接
  13. 个人项目——找水王续
  14. 决斗小游戏代码html,《游戏王:决斗链接》的基础玩法介绍
  15. 《星际穿越》初解析——一部空前绝后的史诗科学巨作
  16. 大学毕业4年-未来展望(1)-商业研究
  17. 字符串实质,sizeof strlen
  18. php设计鸡兔同笼问题解法,鸡兔同笼问题4种解题方法
  19. 常见的网络状态检测及分析工具
  20. 科众集团聚力呈现2019创新服务峰会——“碰撞·革新”

热门文章

  1. 哈工大 大数据算法 频度矩估计-Final AMS 算法
  2. 为什么程序员都吝啬自己的点赞?
  3. 日本记者:奥运会改变一座城市 北京人不再傲慢
  4. 【并行计算】Bitonic Sort(双调排序)基础
  5. html5老是自动退出全屏,JavaScript实现全屏和退出全屏功能
  6. 离心机如何工作——TFN A17CH微量高速冷冻离心机
  7. 跑步锻炼(2020省赛)用Excel表格求解答案8879
  8. js复制图片,支持jpg和png
  9. 数据仓库技术中的MPP
  10. app-调用第三方app-api