juc多线程编程学习
JUC是java.util.concurrent的缩写,java.util.concurrent是在并发编程中使用的工具类。
在以前的解决并发问题,一般是通过Synchronize关键字,现在可以通过juc下的工具类,来解决多线程并发问题。
首先写有个demo:使用synchronized进行上锁
public class Synchronizedemo { 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(); }
}
class Ticket{ private int num = 30; public synchronized void sale(){
if(num > 0){
System.out.println("线程"+Thread.currentThread().getName()+",卖出第"+num+"票");
}
num--;
}
}
Lock
Lock是一个接口
这次我们先使用lock锁,实现提供了比使用同步方法和语句更广泛的锁操作。
它们允许更灵活的结构,可能具有非常不同的属性,并且可能支持多个关联的条件对象,
提供更为细粒化的锁操作。
我们使用ReentrantLock这个实现类来操作锁,这个锁被称为可重入锁,可以理解为厕所门栓。根据官方提供的demo:
It is recommended practice to always immediately follow a call to lock with a try block, most typically in a before/after construction such as: class X {
private final ReentrantLock lock = new ReentrantLock();
// ... public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
现在我们来写个demo:卖票程序:
package com.study.lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class SaleTicket {
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();
}
} class Ticket{ private int num = 30;
Lock lock = new ReentrantLock(); public void sale(){ lock.lock();
try{
if(num > 0){
System.out.println(Thread.currentThread().getName()+"卖出了第"+num--+"还剩有"+(num)+"张票");
}
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
再来另外一个例子:实现一个线程对该变量加1,一个线程对该变量减1,循环交替。
demo1:使用Synchronized实现
class Test{
private int num = 0 ; public synchronized void increase() throws InterruptedException { if (num != 0){
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName()+","+num);
this.notifyAll();
}
public synchronized void desc() throws InterruptedException { if (num == 0){
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName()+","+num);
this.notifyAll();
}
} public class ProduConsumer { public static void main(String[] args) throws Exception { Test test = new Test(); new Thread(()->{
for (int i = 0 ; i < 10 ; i++){
try {
test.increase();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"a").start(); new Thread(()->{
for (int i = 0 ; i < 10 ; i++){
try {
test.desc();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"b").start(); new Thread(()->{
for (int i = 0 ; i < 10 ; i++){
try {
test.increase();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"c").start(); new Thread(()->{
for (int i = 0 ; i < 10 ; i++){
try {
test.desc();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"d").start();
}
}
根据打印结果,我们知道这样会导致数据发生错误,那么这是为什么呢?根据官方文档我们可知,打断和虚假唤醒是可能的,所以我们应该使用循环,而不是if
As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop: synchronized (obj) {
while (<condition does not hold>)
obj.wait(timeout, nanos);
... // Perform action appropriate to condition
}
所以将while替换成if,为什么使用if不行?想一下,如果我们的a线程,进入判断后,执行到 this.wait()方法中断 ; 这时候,c线程一样。进入判断,也中断。
这时候就有两个线程都在等待wait,然后b线程notifyAll对a,c线程唤醒,但由于判断条件是if,他们没有回头再去判断,这时候两个线程都执行num++,所以会出现2了,
所以需要使用while,让它重新回来判断,防止虚假唤醒。
while (num != 0){
this.wait();
}
num++;
demo2:使用lock实现
package com.study.prodConsumer; import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; class Testdemo{
private int num = 0 ;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition(); public void increase() throws InterruptedException { lock.lock();
try{
while (num != 0){
condition.await();
}
num++;
System.out.println(Thread.currentThread().getName()+","+num);
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
} public void desc() throws InterruptedException { lock.lock();
try{
while (num != 0){
condition.await();
}
num--;
System.out.println(Thread.currentThread().getName()+","+num);
condition.signalAll(); }catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
} } } public class ProduConsumerLock {
public static void main(String[] args) {
Test test = new Test(); new Thread(()->{
for (int i = 0 ; i < 10 ; i++){
try {
test.increase();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"a").start(); new Thread(()->{
for (int i = 0 ; i < 10 ; i++){
try {
test.desc();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"b").start(); new Thread(()->{
for (int i = 0 ; i < 10 ; i++){
try {
test.increase();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"c").start(); new Thread(()->{
for (int i = 0 ; i < 10 ; i++){
try {
test.desc();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"d").start();
}
}
Synchronized的wait和lock的await对应,notifyAll和signalAll对应,这里我们需要使用Condition。根据官网对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.
所以我们看看Condition:大概意思就是可以使用Condition来代替Object monitor methods,也就是wait, notify and notifyAll方法,相当于Condition就是Lock的钥匙。
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.
官方写了一个实例告诉我们怎么使用:
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100];
int putptr, takeptr, count; public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
} public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
lock锁还有一大好处就是可以精确通知唤醒:
demo:多线程之间按顺序调用,实现A->B->C 三个线程启动,A打印5次,B打印10次,C打印15次
class ConditionTest{
private int num = 1;//标识 private Lock lock = new ReentrantLock();
//一把锁配三把钥匙
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition(); public void print5(){
lock.lock();
try{
while (num != 1){
condition1.await();
}
for (int i = 0; i < 5 ; i++) {
System.out.println(Thread.currentThread().getName()+(i+1));
}
num = 2;
condition2.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
} public void print10(){
lock.lock();
try{
while (num != 2){
condition2.await();
}
for (int i = 0; i < 10 ; i++) {
System.out.println(Thread.currentThread().getName()+(i+1));
}
num = 3;
condition3.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
} public void print15(){
lock.lock();
try{
while (num != 3){
condition3.await();
}
for (int i = 0; i < 15 ; i++) {
System.out.println(Thread.currentThread().getName()+(i+1));
}
num = 1;
condition1.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
} public class ConditionDemo {
public static void main(String[] args) {
ConditionTest conditionTest = new ConditionTest();
new Thread(()->{
for (int i = 0; i < 10; i++) {
conditionTest.print5();
}
},"A").start(); new Thread(()->{
for (int i = 0; i < 10; i++) {
conditionTest.print10();
}
},"B").start(); new Thread(()->{
for (int i = 0; i < 10; i++) {
conditionTest.print15();
}
},"C").start(); }
}
Synchronized和lock两者区别:
1.首先synchronized是java内置关键字,在jvm层面,Lock的实现类是个java类;
2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
3.synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
4.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
5.synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)
6.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。
CopyOnWriteArrayList:写时复制
在学习集合时,我们知道ArrayList和Hashset是线程不安全的,里面有很多操作公共属性的方法,而且方法上都没有进行加锁操作,所以当多线程并发的时候,会造成数据的错误。
那么怎么解决线称安全?
1》Collections集合工具类对List进行加锁,Collections.synchronizedList
2》可以使用线程安全类Vector
demo:
public class ListTest { public static void main(String[] args) { List<String> list1= Collections.synchronizedList(new ArrayList<>()); //Collections集合工具类 线程安全
List<String> list = new Vector<>();//线程安全 for(int i = 0 ; i < 30 ; i++){ new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(list);
},"thread"+i).start();
}
}
}
我们还可以使用
3》CopyOnWriteArrayList:写时复制
CopyOnWriteArrayList是juc下提供了一种线程安全的集合,称为写时复制,它里面的add方法使用了可重入锁;remove方法也用了可重入锁
原理: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();
}
}
同理hashSet也有这样一个线程安全的CopyOnWriteArraySet
Set<String> set = new CopyOnWriteArraySet<>();//线程安全
HashMap也是不安全的,juc也提供了ConcurrentHashMap保证线程安全
Map<String,String> map = new ConcurrentHashMap<>();//线程安全
FurureTask/Callable
:参考java中创建线程的方式
JUC中还提供了其他的功能强大的辅助类:
CountDownLatch:倒计时计数器
看demo:倒计时计数
class Test{
public void start() throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(5);
for (int i = 5; i >= 1; i--) {
new Thread(()->{
System.out.println(Thread.currentThread().getName());
countDownLatch.countDown();
},""+i).start();
}
countDownLatch.await();
System.out.println("预备跑");
}
}
public class countDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
test.start();
}
}
原理:
* CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞。
* 其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞),
* 当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行。
CyclicBarrier:循环栅栏或加法计数器
demo:七个葫芦娃合体
public class CycleBarrierDemo { public static void main(String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{ System.out.println("**葫芦娃合体**"); }); for (int i = 0; i < 7 ; i++) {
final int tem = i;
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"第"+tem+"葫芦娃");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
原理:
CyclicBarrier的字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是,
让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有
被屏障拦截的线程才会继续干活。 线程进入屏障通过CyclicBarrier的await()方法。
Semaphore:信号灯
demo:七辆车占三个车位
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);//三个车位 for (int i = 0; i < 7 ; i++) {
new Thread(()->{
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"抢到了车位");
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"离开了车位");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
},""+i).start();
}
}
原理:
在信号量上我们定义两种操作:
acquire(获取) 当一个线程调用acquire操作时,它要么通过成功获取信号量(信号量减1),要么一直等下去,直到有线程释放信号量,或超时。
release(释放)实际上会将信号量的值加1,然后唤醒等待的线程,信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。
ReadWriteLock:读写锁
根据官方文档,大概意思是读的时候可以多个线程同时进行,写的时候只能由一个线程操作,读写锁可以提供更为细粒化的锁机制
A ReadWriteLock maintains a pair of associated locks, one for read-only operations and one for writing.
The read lock may be held simultaneously by multiple reader threads, so long as there are no writers. The write lock is exclusive.
demo:我们对map进行读写操作,读的时候都可以读,写的时候只能一个一个的写
package com.study.lock; import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; class Test{
private volatile Map<Integer,String> map = new HashMap<>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); public void put(Integer num) {
readWriteLock.writeLock().lock();
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"开始写入");
map.put(num, "" + num);
System.out.println(Thread.currentThread().getName()+"写入结束");
}catch (Exception e){
e.printStackTrace();
}finally {
readWriteLock.writeLock().unlock();
} } public void read(Integer num){
readWriteLock.readLock().lock();
try {
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+"开始读入");
map.get(num);
System.out.println(Thread.currentThread().getName()+"读入完成");
}catch (Exception e){
e.printStackTrace();
}finally {
readWriteLock.readLock().unlock();
} } }
public class ReadWriteLockDemo { public static void main(String[] args) {
Test test = new Test();
for (int i = 1; i < 5 ; i++) {
final Integer temp = i;
new Thread(()->{
test.put(temp);
},""+i).start();
} for (int i = 1; i < 5 ; i++) {
final Integer temp = i;
new Thread(()->{
test.read(temp);
},""+i).start();
}
}
}
分支合并框架
Fork:把一个复杂任务进行分拆,大事化小
Join:把分拆任务的结果进行合并
例如我们需要计算从1加到100的数据,如果一个人一个一个数相加来算就太麻烦,把任务分出去
找十个人来算,第一个人算1到10,依次类推,最后把结果合并,这样就会减少任务的复杂性。
官方api提供了计算斐波纳契数列的例子:
A recursive result-bearing ForkJoinTask.
For a classic example, here is a task computing Fibonacci numbers: class Fibonacci extends RecursiveTask<Integer> {
final int n;
Fibonacci(int n) { this.n = n; }
Integer compute() {
if (n <= 1)
return n;
Fibonacci f1 = new Fibonacci(n - 1);
f1.fork();
Fibonacci f2 = new Fibonacci(n - 2);
return f2.compute() + f1.join();
}
}
我们需要继承RecursiveTask类,我们还需要了解ForkJoinPool(分支合并池 ),可以理解为线程池,向池中提供任务。
通过forkJoinTask.get()获取到compute()方法最终的值;ForkJoinTask是Futher的实现类,
我们知道FutureTask也是使用get()方法,来获取到call()方法的返回值,FutureTask也是Future的实现类 demo:
//RecursiveTask 递归任务:继承后可以实现递归(自己调自己)调用的任务
class MyTask extends RecursiveTask<Integer>{ private static final int RESULT_VALUE = 10; private int begin;
private int end ;
private int result ; public MyTask(int begin, int end) {
this.begin = begin;
this.end = end;
} @Override
protected Integer compute() { if((end - begin) < RESULT_VALUE){
for (int i = begin; i <= end ; i++) {
result = result + i;
}
}else {
int mid = (end + begin) / 2;
MyTask myTask01 = new MyTask(begin,mid);
MyTask myTask02 = new MyTask(mid+1,end);
myTask01.fork();
myTask02.fork();
result = myTask01.join()+myTask02.join();
}
return result;
}
}
public class ForkJoinDemo { public static void main(String[] args) throws ExecutionException, InterruptedException {
MyTask myTask = new MyTask(0,50000); ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Integer> forkJoinTask = pool.submit(myTask);//向池中提交任务
System.out.println(forkJoinTask.get());
pool.shutdown();
} }
异步回调
CompletableFuture提供了很多处理异步的方法,有两种形式:无返回值,有返回值。
demo:
/**
* 异步回调
*/
public class CompletableDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException { /* CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
System.out.println("没有返回值的异步回调");
});
completableFuture.get();*/ CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(()->{
System.out.println("*********\n 有返回值的异步回调");
//int n = 10/0;
return 1024;
}); completableFuture1.whenComplete((t,u)->{
System.out.println(t);
System.out.println(u);
}).exceptionally(t->{
System.out.println("异步回调发生异常");
System.out.println(t);
return 1025;
})get();
}
}
CompletableFuture.runAsync()执行异步方法,没有返回值,根据方法,它需要有个Runnable
public static CompletableFuture<Void> runAsync(Runnable runnable) {
return asyncRunStage(asyncPool, runnable);
}
CompletableFuture.supplyAsync()执行异步方法,有返回值,根据方法,它需要有个Supplier,
Supplier是一个供给型的函数接口,没有传入值有返回值。
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
return asyncSupplyStage(asyncPool, supplier);
}
最后使用completableFuture1.whenComplete().exceptionally()方法,就是当异步任务完成后如果没有发生异常执行第一个方法,发生异常执行第一个方法。
调用get()来获取返回值。completableFuture1.whenComplete()里面接受BiConsumer,消费型的函数接口,接受两个参数,没有返回值
exceptionally()方法,接受Function,函数式接口,有传入值和返回值。
关于函数式接口可以参考这里:java内置四大函数接口参考:java新特性stream
juc下还有很多关于并发编程的接口和类,在java.util.concurrent.atomic
包下提供了很多的原子操作类:
例如:AtomicInteger类
先得到当前值再将当前值进行+1操作:它是线程安全的
getAndIncrement public final int getAndIncrement() Atomically increments by one the current value. Returns:
the previous value
demo:
public class AtomicIntegerDemo {
private int num = 0;
public static void main(String[] args) {
Test test = new Test();
new Thread(()->{ test.incre(); },"A").start();
new Thread(()->{ test.incre(); },"B").start();
new Thread(()->{ test.incre(); },"C").start();
}
}
class Test{
private AtomicInteger num = new AtomicInteger(0);
public void incre(){
System.out.println(Thread.currentThread().getName()+"得到的值"+num.getAndIncrement()+",并加1");
}
}
它的原理是采用了CAS算法。
待更。。。。。。。。。。
juc多线程编程学习的更多相关文章
- JUC并发编程学习笔记
JUC并发编程学习笔记 狂神JUC并发编程 总的来说还可以,学到一些新知识,但很多是学过的了,深入的部分不多. 线程与进程 进程:一个程序,程序的集合,比如一个音乐播发器,QQ程序等.一个进程往往包含 ...
- 多线程编程学习笔记——async和await(一)
接上文 多线程编程学习笔记——任务并行库(一) 接上文 多线程编程学习笔记——任务并行库(二) 接上文 多线程编程学习笔记——任务并行库(三) 接上文 多线程编程学习笔记——任务并行库(四) 通过前面 ...
- 多线程编程学习笔记——async和await(二)
接上文 多线程编程学习笔记——async和await(一) 三. 对连续的异步任务使用await操作符 本示例学习如何阅读有多个await方法方法时,程序的实际流程是怎么样的,理解await的异步 ...
- 多线程编程学习笔记——async和await(三)
接上文 多线程编程学习笔记——async和await(一) 接上文 多线程编程学习笔记——async和await(二) 五. 处理异步操作中的异常 本示例学习如何在异步函数中处理异常,学习如何对多 ...
- 多线程编程学习笔记——使用异步IO(一)
接上文 多线程编程学习笔记——使用并发集合(一) 接上文 多线程编程学习笔记——使用并发集合(二) 接上文 多线程编程学习笔记——使用并发集合(三) 假设以下场景,如果在客户端运行程序,最的事情之一是 ...
- 多线程编程学习笔记——编写一个异步的HTTP服务器和客户端
接上文 多线程编程学习笔记——使用异步IO 二. 编写一个异步的HTTP服务器和客户端 本节展示了如何编写一个简单的异步HTTP服务器. 1.程序代码如下. using System; using ...
- 多线程编程学习笔记——异步调用WCF服务
接上文 多线程编程学习笔记——使用异步IO 接上文 多线程编程学习笔记——编写一个异步的HTTP服务器和客户端 接上文 多线程编程学习笔记——异步操作数据库 本示例描述了如何创建一个WCF服务,并宿主 ...
- 多线程编程学习笔记——使用异步IO
接上文 多线程编程学习笔记——使用并发集合(一) 接上文 多线程编程学习笔记——使用并发集合(二) 接上文 多线程编程学习笔记——使用并发集合(三) 假设以下场景,如果在客户端运行程序,最的事情之一是 ...
- [Java123] JDBC and Multi-Threading 多线程编程学习笔记
项目实际需求:DB交互使用多线程实现 多线程编程基础:1.5 :( (假设总分10) 计划一个半月从头学习梳理Java多线程编程基础以及Oracle数据库交互相关的多线程实现 学习如何通过代码去验证 ...
随机推荐
- PHP全栈学习笔记27
数组概述,类型,声明,遍历,输出,获取数组中最后一个元素,删除重复数组,获取数组中指定元素的键值,排序,将数组中的元素合成字符串. 数组概述,数组是存储,管理和操作一组变量. 数组类型为一维数组,二维 ...
- [Go] 基础系列一: for-select中的break、continue和return
break select中的break,类似c系列中的break,break后的语句不执行 for和select一同使用,有坑 break只能跳出select,无法跳出for package test ...
- Java学习日记基础篇(九) —— 集合框架,泛型,异常
集合框架 有事我们会需要一个能够动态的调整大小的数组,比如说要添加新员工但是数组已经满了,并且数组的大小是在定义的时候定死的,所以我们就需要一个能够动态调整大小的数组或者用链表解决,而java中提供了 ...
- Hadoop hadoop的介绍和几种模式
Hadoop简介 Hadoop软件库是一个开源框架,允许使用简单的编程模型跨计算机集群分布式处理大型数据集.它旨在从单个服务器扩展到数千台计算机,每台计算机都提供本地计算和存储.库本身不是依靠硬件来提 ...
- 运维管理SLA
主要三个概念: SLI 服务关键量化指标,即测试哪些指标,如何测等 SLO :服务等级目标,即要达到哪些目标,如设备正常率3个9.4个9等,即99.9% SLA: 服务等级协议,即如果未完成SLO中 ...
- beyond compare秘钥被禁
错误提示:This license key has been revoked xxxxx 即: Windows 系统: 解决方法: 删除以下目录中的所有文件即可. C:\Users\Administr ...
- 2018-2019-2 网络对抗技术 20165202 Exp7 网络欺诈防范
博客目录 一.实践目标 二.实践内容 简单应用SET工具建立冒名网站 (1分) ettercap DNS spoof (1分) 结合应用两种技术,用DNS spoof引导特定访问到冒名网站.(1.5分 ...
- Alpha项目冲刺! Day7-总结
各个成员今日完成的任务 林恩:任务分工,博客撰写,辅助调试安卓端 李震:修改安卓框架,调试安卓端 胡彤:完善服务端,搭建服务端数据库 李杰:研究了测试工具UiAutomator和MonkeyRunne ...
- 更换镜像加快python pip 安装扩展库的速度
一些镜像源: 清华大学 https://pypi.tuna.tsinghua.edu.cn/simple/ 阿里云 http://mirrors.aliyun.com/pypi/simple/ 中国科 ...
- 使用Qt Creator作为Linux IDE,代替Vim:实现两台Linux电脑远程部署和gdb调试(一台电脑有桌面系统,一台电脑无桌面系统)
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/libaineu2004/article/details/62423830 尊重作者,支持原创,如 ...