java并发与多线程

()SynchronizedLock区别   --都是可重入锁

答:Lock能够实现Synchronized的所有功能(互斥性、内存可见性)Synchronized自动释放锁,Lock必须手动释放,将unLock()放到finally,Synchronized一直等待直到获取锁,Lock可以在指定时间获取不到锁就自动放弃(tryLock),Synchronized无法响应中断Lock可以响应中断(LockInterruptibly,每次申请锁前,判断线程中断标识是否为true,如果是抛出中断异常)

   Synchronized非公平锁 synchronized同步基础:java中每个对象都有一把锁。

()java实现同步机制的3方法1.synchronized  2.lock  3.waitnotify

()CAScompare-and-set-->是一种乐观锁

答: CAS 建立在硬件指令集”上,是一个原子操作。(更高效,c/c++都有

CAS操作的三个操作数:

        1.变量内存地址D

        2.变量旧的预期值V

        3.变量的新值N

       当预期值VD相等的时候,将D更新为新值N,否则不更新。

   例:if(a==b){ a++;},如果a++之前,a的值被改变,a的值存在问题。

      为了保证结果正确,除了用解决,可以用CAS

               While(true){

                    int expect=a;

                    if(compareAndSet(&a,expect,a+1)){

                       return;

                    }

                }


()CAS的缺点?解决ABA问题?

答:存在ABA问题。就是一个变量VCAS前读取的值是ACAS准备赋值的时候检查其仍然是A,可能在这段期间V的值被修改成B,然后又改回ACAS操作误以为没有被修改过。对象添加版本号,每次变量更新的时候对象的版本号加1(判断版本号和CAS合一起是一个原子操作)。

()什么是Lock的AQS(AbstractQueuedSynchronizer抽象队列同步器)

答:AQS内部会保存一个状态变量state,通过CAS修改该变量的值,修改成功的线程表示获取到该锁,没有修改成功,或者发现状态state已经是加锁状态,则通过一个Waiter对象封装线程,添加到等待队列中,并挂起等待被唤醒

()自旋锁-->工作于多处理器环境

答:在线程没有取得锁的时候,不被挂起(不引起上下文切换),而转去执行一个空循环,若在若干次空循环后,线程如果获得锁,则继续执行,如果依然不能获得锁,才会被挂起。 优点:避免线程的切换代价

()原子类的实现

答:CAS死循环尝试更新AtomicInteger保证增加、减小操作的原子性

设计一个并发安全的计数器

aLongAdder代替原子类AtomicLong实现计数器


bInnodb创建计数器表



()公平锁、非公平锁

答:公平锁:先申请锁的线程先获得锁。

非公平锁:随机挑选线程获得锁。

如何确保N个线程可以访问N个资源同时又不导致死锁?

答:指定获取锁的顺序,强制线程按指定顺序获取锁。

()CountDownLatch       --不可重复使用

答:倒计数的锁,当计数为0时,唤醒线程。

      (一个线程等待一组线程执行完成)

重要方法:

   countDown()-->计数器减1,当计数为0时,唤醒线程

   await()-->当计数器大于0时,阻塞线程。

()CyclicBarrier     --可重复使用

答:关于计数器的锁

    (将一组线程集中到某一点再开始执行)

   重要方法:

            await()-->计数器减1,当计数器大于0时,阻塞当

                    前线程,为0时,唤醒所有等待线程。

java计数器:

   小顶堆存储定时事件,线程不断从堆顶取出事件判断是需要立即执行还是等待一段时间(等的时间:wait(当前时间-堆顶时间),如果插入堆的事件就是最小的,notify唤醒线程判断是否需要立即执行)。


多线程

线程状态转换图:


7sleep()wait()

答:sleep(静态方法)属于Thread类,sleep不释放锁,可以自动唤醒,wait()属于object类,wait方法释放锁,需要notify/notifyAll唤醒。

Wait循环中调用

Synchronized(obj) {While(condition does not hold) obj.wait();}

()yield->使当前运行线程处于就绪状态,不释放锁。

进程(操作系统调度):每个进程都有独立的代码和数据空间(进程上下文),进程的切换会有较大的开销,一个进程包含 1-n个线程。(进程是资源分配的最小单位)。

线程(操作系统调度)同一进程中的线程共享代码和数据空间,每个线程有独立运行栈和程序计数器,线程切换开销小。(线程是cpu调度的最小单位)

              进程                                                                    线程

占用内存多,创建、销毁、切换代价大    占用内存少,创建、销毁、切换代价小

-------需要频繁创建销毁的优先线程。(web服务器处理连接)

-------需要大量计算的优先线程。(cpu切换频繁)

每个java.lang.Thread类的实例就代表一个线程。

Java的线程调度方式为抢占式。一个线程用完CPU之后,操作系统根据线程优先级、线程饥饿情况等算出一个总的优先级并分配时间片给某个线程。

HotSpot(1:1):Java线程被映射到系统的原生线程上,java中没有办法强制启动一个线程,它是由线程调度器控制着。

3种创建线程方式:   --一个线程异常终止,对其他线程无影响

1)继承Thread类            默认优先级5

2)实现Runnable接口

3)使用CallableFuture创建线程

前两种无法获取线程执行结果,Callable产生结果,Future获得结果



检测一个线程是否拥有某个对象的锁:ThreadholdsLock(Object)方法

Java中两类线程:用户线程、守护线程

    唯一区别:JVM何时退出

守护线程为用户线程提供服务(最典型的GC),当JVM实例中只存在守护线程时,JVM会退出,只要存在用户线程,JVM就不会离开。

钩子线程每个进程都可以注册多个钩子线程,钩子线程在进程退出前执行。

Runtime.getRuntime().addShutdownHook(Thread) IdentityHashMap<Thread,Thread>

钩子线程并发执行

线程类的静态块构造方法在哪个线程调用?

答:new这个线程类所在的线程调用。而run方法才被线程自身调用。

Java进程间通信:

1.共享内存MappedByteBuffer

2.Rmi-->本质还是socket

Join()方法

(java.lang.Thread类的实例方法):thread.join(),线程thread执行完以后,调用此条语句的线程才继续执行(参数为0)

原理:调用thread.join()时,join方法内部调用wait()方法,此时调用此条语句的线程会被放入thread对象的等待池中,当线程完成后,调用notifyAll()唤醒线程。


Java中断:每个线程对象中都有一个boolean类型的标识(只是一个标志位,本身并不影响线程中断与否),代表是否有中断请求,调用线程对象的interrupt()方法,可以将对应线程对象的标识置为true,通过判断线程的中断标识可以控制线程的执行。(当线程的中断标志位为true后,因为调用sleepwaitjoin方法而导致的阻塞会抛出InterruptedException异常

(例如当线程t1想中断线程t2,只需要在线程t1中调用t2interrupt(),将线程t2的中断标识置为truet2执行中判断该标识来决定操作。)

终止线程的方法1.stop()强行退出,释放锁过时方法,强行把执行到一半的线程终止,可能引起数据不一致的问题。(例如更新数据到一半)

                             2.interrupt()方法

线程的run方法无法抛出异常(throws),但线程却可能因为一个异常而终止,导致无法回收一些系统资源,可以为线程设置一个UncaughtExceptionHandler,当出现一个未捕获异常时,JVM会使用该对象进行处理。

线程处理不可捕捉异常:查找线程对象的未捕获异常处理器,如果不存在查找线程对象所在线程组的未捕获异常处理器,如果不存在查找默认的未捕获异常处理器,如果都不存在将堆栈异常记录打印到控制台,退出程序。





猜你喜欢

转载自blog.csdn.net/gaibian_one/article/details/77949754