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数据库交互相关的多线程实现 学习如何通过代码去验证 ...
随机推荐
- DIV半透明层
想使用DIV半透明层时 只需加一个filter:alpha(Opacity=80);-moz-opacity:0.5;opacity: 0.5 0.5为半透明系数 使用前 使用后 style=& ...
- 在jsp页面,如何让一个button具有submit的功能
<input type=button value="提交" οnclick=submit()> <script language=javascript>fu ...
- RDMA Programming - Base on linux-rdma
RDMA Programming - Base on linux-rdma 首页分类标签留言关于订阅2017-11-08 | 分类 Network | 标签 RDMA RoCE Linux-RD ...
- 第九届蓝桥杯(A组填空题)
1.标题:分数 1/1 + 1/2 + 1/4 + 1/8 + 1/16 + …. 每项是前一项的一半,如果一共有20项, 求这个和是多少,结果用分数表示出来. 类似: 3/2 当然,这只是加了前2项 ...
- 传统PC机I/O位址空间范围
空间范围是0x000--0x3FF,有1024个I/O端口位址可供使用! 使用EISA或PCI等汇流排结构的现代PC机,有64KB的I/O位址空间可供使用.在普通Linux系统下透过查看/proc/i ...
- python 监视和控制鼠标键盘的输入(使用pynput 而非pyhook)
百度上搜到的文章大多基于pyhook, pip不能直接安装,托管在sourceForge上的代码仓库也找不到. google上发现可以使用pynput,貌似控制更为简单,而且可以直接使用pip安装 示 ...
- eclipse 点击 new window 后,关闭新增的窗口
点击右上角的 X 关闭,不要点击 exit 退出
- GFS中元数据的管理
GFS 元数据(metadata)中包含三部分: GFS元数据的管理方式: 1.文件的命名空间和块的命名空间: 采用持久化的方式. 对于文件和块的命名空间以及从文件到块的映射:通过向操作日志登记修改而 ...
- qt QThread
QThread类提供了一个平台无关的方式来管理线程. 一个QThread对象在程序控制中管理一个线程.线程在run()中开始执行.默认情况下,run()通过调用exec()启动事件循环并在线程里运行一 ...
- Oracle 11c下载 及连接到OracleDB的简单程序
Oracle官网总是不太贴心.还是网友贴心. https://pan.baidu.com/s/1ZCFLUi4Ti_WUYOFR3gB2dA 是11g版本下载包,下载下来解压就能用了. 安装完毕后,驱 ...