1.java并发包括
①synchronized(保证数据可见性原子性)
②volatile(防止JVM指令重排;指示变量共享不稳定需要每次都从内存中获取从而保证变量可见性)
③threadLoocal(底层是threadLoocalMap.put(当前线程,值)
④线程池(作用:降低资源消耗,提高响应速度,提高线程可管理性, executor.execute(new MyRunnable(""+i))😉
⑤Atomic(
AtomicInteger:getAndSet(int newValue)
getAndIncrement()//获取当前的值,并自增
getAndDecrement() //获取当前的值,并自减
compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)
getAndAdd(int delta) //获取当前的值,并加上预期的值
AtomicInteger 类主要利用CAS (compare and swap) + volatile 和 native 方法来保证原子操作,从而避免 synchronized 的高开销,执行效率大为提升。
)
⑥AQS( CLH 队列锁实现的,即将暂时获取不到锁的线程加入到队列中。状态信息通过 protected 类型的 getState,setState,compareAndSetState 进行操作
tryAcquire(int)//独占方式。尝试获取资源,成功则返回true,失败则返回false。
tryRelease(int)//独占方式。尝试释放资源,成功则返回true,失败则返回false。
tryAcquireShared(int)//共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
tryReleaseShared(int)//共享方式。尝试释放资源,成功则返回true,失败则返回false。
)
PS:并发编程:原子性 有序性 可见性
2.synchronized
(1)认识:
重量级锁,synchronize保证同一时刻只有一个线程执行,因为synchronize依赖于底层的操作系统的 Mutex Lock 来实现的,线程上下文切换需要从用户态转换到内核态,这需要操作系统完成,因此耗时较大
(2)synchronized使用:
①加在静态方法
synchronized static void method() {
//业务代码
}
②加在静态代码块
synchronized (this) {
//业务代码
}
③普通方法
synchronized void method() {
//业务代码
}
(3)synchronized底层实现
synchronized 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。
3.AQS
Exclusive(独占):只有一个线程能执行,如 ReentrantLock
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
//默认false ,false代表非公平锁
private static final Lock lock=new ReentrantLock(true);
public static void main(String[] args) {
// TODO Auto-generated method stub
for(int i=1;i<8;i++)
{
new Thread(()->test(),"线程"+i).start();
}
}
public static void test()
{
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+" try to lock");
TimeUnit.SECONDS.sleep(2);
// Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally
{
System.out.println(Thread.currentThread().getName()+" release lock");
lock.unlock();
}
}
}
Share(共享):多个线程可同时执行,如 CountDownLatch、Semaphore、 CyclicBarrier、ReadWriteLock
(1)CountDownLatch(等待线程数量足够时才往下执行)
import java.util.concurrent.CountDownLatch;
public class CountDownLatchdemo {
static CountDownLatch countDownLatch = new CountDownLatch(5);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
Thread.sleep(10000);
System.out.println(Thread.currentThread().getName()+"开始等待");
countDownLatch.countDown();//计数器-1
System.out.println("计数器"+countDownLatch.getCount());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
countDownLatch.await();// 主线程在阻塞,当计数器==0,就唤醒主线程往下执行。
System.out.println("当前等待线程数量已达到5即计数器为0,开始往下执行");
}
}
(2)Semaphore(同一时刻多个线程执行)
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SemaphoreDemo {
private static final Semaphore sp=new Semaphore(10);
public static void main(String[] args) {
// TODO Auto-generated method stub
for(int i=1;i<18;i++)
{
new Thread(()->test(),"线程"+i).start();
}
}
public static void test()
{
try {
System.out.println(Thread.currentThread().getName()+" try to lock");
sp.acquire();
TimeUnit.SECONDS.sleep(20);
// Thread.sleep(100);
System.out.println("并发线程数量="+(10-sp.availablePermits())+";queue length="+sp.getQueueLength());
System.out.println("开始执行业务逻辑中.......");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally
{
System.out.println(Thread.currentThread().getName()+" release lock");
sp.release();
}
}
}
(3) CyclicBarrier
其实和CountDownLatch蛮像的,不过CountDownLatch 是一次性的,CyclicBarrier 是可循环利用的
为什么说CountDownLatch 是一次性的呢?看下图CountDownLatch 运行效果图,
"当前等待线程数量已达到5即计数器为0,开始往下执行"这句话只执行了一次,也就是说计数器到0之后不会重新变成5
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierdemo {
static int CyclicBarrier_capacity=5;
static CyclicBarrier CB = new CyclicBarrier( CyclicBarrier_capacity);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 2*CyclicBarrier_capacity; i++) {
new Thread(() -> {
try {
// Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"开始等待"+"等待数量"+ CB.getNumberWaiting()+";parties="+CB.getParties());
CB.await();
System.out.println("当前等待线程数量已达到5即计数器为0,开始往下执行");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
PS:注意比较CountDownLatch和CyclicBarrier:
(1) CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待。
(2) CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。
演示效果:
(1)运行 CountDownLatchdemo,到了后面计数器一直为0
(2)运行CyclicBarrierdemo,可见计数器没有一直为0,而是0->4周期性变化
https://www.cnblogs.com/skywang12345/p/3533995.html
(4)ReadWriteLock
以ReetrantReadWriteLock演示
private static ReentrantReadWriteLock rwl=new ReentrantReadWriteLock();
//四种方法
//加写锁
rwl.writeLock().lock();
//释放写锁
rwl.writeLock().unlock();
//加读锁
rwl.readLock().lock();
//释放读锁
rwl.readLock().unlock();
完整代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReentrantReadWriteLockDemo {
private static ReentrantReadWriteLock rwl=new ReentrantReadWriteLock();
public static void main(String[] args) {
ExecutorService es=Executors.newCachedThreadPool();
//开启2个线程,1个执行读操作 1个执行写操作
es.execute(new Runnable()
{
@Override
public void run() {
// TODO Auto-generated method stub
myreadlock(Thread.currentThread());
}
}
);
es.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
writemylock(Thread.currentThread());
}
}
);
}
//写锁具体实现
protected static void writemylock(Thread currentThread) {
// TODO Auto-generated method stub
rwl.writeLock().lock();
if(!rwl.isWriteLocked())
{
System.out.println("写锁");
}//end if
try {
//处理事务
for(int i=0;i<3;i++)
{
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()+"写入数据"+i);
}//end for
}//end try
catch(Exception e) {}
finally {
//事务执行完释放锁
rwl.writeLock().unlock();
System.out.println("事务执行完释放写锁");}//end finally
}
//读锁具体实现
protected static void myreadlock(Thread currentThread) {
// TODO Auto-generated method stub
rwl.readLock().lock();
if(!rwl.isWriteLocked())
{
System.out.println("没有写锁可以加上读锁");
}// end if
try {
//处理事务
for(int i=0;i<3;i++)
{
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()+"读数据"+i);
}//end for
}catch(Exception e) {}
finally {
//事务执行完释放锁
rwl.readLock().unlock();
System.out.println("事务执行完释放读锁");}//end finally
}
}
总结ReetrantReadWriteLock
①.Java并发库中ReetrantReadWriteLock实现了ReadWriteLock接口并添加了可重入的特性
②.ReetrantReadWriteLock读写锁的效率明显高于synchronized关键字
③ReetrantReadWriteLock读写锁的实现中,读锁使用共享模式;写锁使用独占模式,换句话说,读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的
④ReetrantReadWriteLock读写锁的实现中,需要注意的,当有读锁时,写锁就不能获得;而当有写锁时,除了获得写锁的这个线程可以获得读锁外,其他线程不能获得读锁
作者:itbird01
链接:https://www.jianshu.com/p/9cd5212c8841
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
4.线程池
5.Atomic
6.ThreadLocal
7.volatile
8.扩展
hshamap->concurrenthashmap
arraylist-> CopyOnWriteArrayList
//编译
javac D:\eclipse-javaweb\eclipse-woekspace\Exam\src\com\multRunnable\SynchronizedDemo.java
//反汇编
javap -c -s -v -l D:\eclipse-javaweb\eclipse-woekspace\Exam\src\com\multRunnable\SynchronizedDemo.class