synchronized, AtomicLong和LongAdder效率比较
在高并发下, AtomicLong比synchronized快. AtomicLong属于无锁.
而LongAdder又比AtomicLong快, LongAdder内部是分段锁, LongAdder在AtomicLong的基础上将单点的更新压力分散到各个节点,在高并发的时候通过分散提高了性能.
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
public class Timecompare {
static long count2 = 0L;
static AtomicLong count1 = new AtomicLong();
static LongAdder count3 = new LongAdder();
public static void main(String[] args) {
Thread[] threads = new Thread[1000];
for(int i = 0;i<threads.length;i++) {
threads[i]=new Thread(()->{
for(int k=0;k<10000;k++)count1.incrementAndGet();
});
}
try {
long start = System.currentTimeMillis();//系统时间, 单位为毫秒
for(Thread t:threads)t.start();
for(Thread t:threads)t.join();
long end = System.currentTimeMillis();
System.out.println("Atomic:"+count1.get()+" time "+(end-start));
}
catch(InterruptedException e) {
e.printStackTrace();
}
//----------------------------------------------------------------
Object lock = new Object();
for(int i=0;i<threads.length;i++) {
threads[i]=
new Thread(new Runnable(){
@Override
public void run() {
for(int k=0;k<10000;k++)
synchronized(lock) {
count2++;
}
}
});
}
try {
long start = System.currentTimeMillis();//系统时间, 单位为毫秒
for(Thread t:threads)t.start();
for(Thread t:threads)t.join();
long end = System.currentTimeMillis();
System.out.println("Synchronized :"+count2+" time "+(end-start));
}
catch(InterruptedException e) {
e.printStackTrace();
}
//--------------------------------------------------------------------
for(int i=0;i<threads.length;i++) {
threads[i] = new Thread(()->{
for(int k=0;k<10000;k++)count3.increment();
});
}
try {
long start = System.currentTimeMillis();//系统时间, 单位为毫秒
for(Thread t:threads)t.start();
for(Thread t:threads)t.join();
long end = System.currentTimeMillis();
System.out.println("LongAdder :"+count3.longValue()+" time "+(end-start));
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
}
Atomic:10000000 time 216
Synchronized :10000000 time 904
LongAdder :10000000 time 82
这种效率测试并不是十分准确.
ReentrantLock
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
Lock lock = new ReentrantLock();
void m1() {
// boolean locked = false;
try {
lock.lock();
// locked = lock.tryLock(1,TimeUnit.SECONDS);
for(int i = 0;i<6;i++) {
TimeUnit.SECONDS.sleep(1);
System.out.println(i);
}}
catch(InterruptedException e) {
e.printStackTrace();
}
finally {
lock.unlock();
}
}
//使用trylock进行尝试锁,不管锁定与否,方法都将继续执行, 可以根据trylock的返回值来判断是否锁定,也可以指定trylock的时间,由于trylock(time)抛出异常,所以要注意unlock
void m2(){
// boolean locked = lock.tryLock();
// if(locked)lock.unlock();
boolean locked = false;
try {
locked = lock.tryLock(5,TimeUnit.SECONDS);
System.out.println("m2..."+locked);
}
catch(InterruptedException e) {
e.printStackTrace();
}
finally {
if(locked)lock.unlock();
}
}
public static void main(String[] args) {
lock01 l01 = new lock01();
new Thread(l01::m1).start();
try {
TimeUnit.SECONDS.sleep(1);
}
catch(InterruptedException e) {
e.printStackTrace();
}
new Thread(l01::m2).start();
}
}
lockInterruptibly
使用reentrantlock还可以调用lockInterruptibly方法,可以对线程interrupt方法作出相应.
可以被打断的加锁, lock.lockInterruptibly(); 可以对interrupt()方法做出相应.
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Thread t1 = new Thread(){
public void run(){
try {
lock.lockInterruptibly();
System.out.println("t1..."+locked);
}
catch(InterruptedException e) {
e.printStackTrace();
}
finally {
lock.unlock();
}
}};
t1.start();
try{
Thread.sleep(10000); //等待10秒
}
catch(InterruptedException){
e.printStackTrace();
}
t1.interrupt(); //打断t1的等待
}
公平和非公平的切换
ReentrantLock默认是非公平锁
private static ReentrantLock lock = new ReentrantLock(true);//公平锁
ReentrantLock的底层实现也是cas
CountDownLatch
倒数门栓
CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成一些任务,然后在CountDownLatch上等待的线程就可以恢复执行接下来的任务。
import java.util.concurrent.CountDownLatch;
public static void main(String[] args) {
usingCountDownLatch();
usingJoin();
}
private static void usingCountDownLatch() {
Thread[] threads = new Thread[100];
CountDownLatch latch = new CountDownLatch(threads.length);
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(()->{
int result = 0;
for (int j = 0; j < 10000; j++) {
latch.countDown(); //等待每个线程结束时, latch.countDown() 等
}
});
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
try { //作用: 等待某些线程结束
latch.await(); //门栓在这里锁住门
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("latch结束");
}
private static void usingJoin() {
Thread[] threads = new Thread[100];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(()->{
int result = 0;
for (int j = 0; j < 10000; j++) result +=j;
});
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
for (int i = 0; i < threads.length; i++) {
try {
threads[i].join();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("join 结束");
}
public void countDown()
递减锁存器的计数,如果计数到达零,则释放所有等待的线程。如果当前计数大于零,则将计数减少.
public boolean await(long timeout,TimeUnit unit) throws InterruptedException
使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。如果当前计数为零,则此方法立刻返回true值。
如果当前计数大于零,则出于线程调度目的,将禁用当前线程,且在发生以下三种情况之一前,该线程将一直出于休眠状态:
由于调用countDown()方法,计数到达零;或者其他某个线程中断当前线程;或者已超出指定的等待时间。
如果计数到达零,则该方法返回true值。
如果当前线程,在进入此方法时已经设置了该线程的中断状态;或者在等待时被中断,则抛出InterruptedException,并且清除当前线程的已中断状态。
如果超出了指定的等待时间,则返回值为false。如果该时间小于等于零,则该方法根本不会等待。
参数:
timeout-要等待的最长时间
unit-timeout 参数的时间单位
返回:
如果计数到达零,则返回true;如果在计数到达零之前超过了等待时间,则返回false
抛出:
InterruptedException-如果当前线程在等待时被中断
CyclicBarrier
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class lock04 {
public static void main(String[] args) {
//CyclicBarrier循环栅栏, 什么时候人满了, 栅栏开启20个人出去了, 栅栏又关上等待下一波20人
CyclicBarrier barrier = new CyclicBarrier(20, new Runnable(){//第一个参数是当达到20时,调用runnable
@Override
public void run() { System.out.println("满人,发车");}
});
// CyclicBarrier barrier1 = new CyclicBarrier(20);//如果没有runnable,就只是单纯的等20人就开启栅栏
// CyclicBarrier barrier2 = new CyclicBarrier(20,()->System.out.println("满人,发车")); //lambda写法
for (int i = 0; i < 100; i++) {
new Thread(()->{
try {//100个线程, barrier等待, 等栅栏开启然后启动, 20个线程一波
barrier.await();
}catch(InterruptedException e) {
e.printStackTrace();
}catch(BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
限流: 流量的入口很多, 限制出口Guava RateLimiter
读写锁
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class lock06 {
static Lock lock = new ReentrantLock();
private static int value;
static ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); //读写锁
static Lock readLock = readWriteLock.readLock(); //当一个线程读的时候, 其他线程也能够读,理论上10个线程能同时完成读操作 脏读
static Lock writeLock = readWriteLock.writeLock();
public static void read(Lock lock) {
try {
lock.lock();
Thread.sleep(1000);
System.out.println("read over");
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public static void write(Lock lock,int v) {
try {
lock.lock();
Thread.sleep(1000);
value = v;
System.out.println("write over");
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public static void main(String[] args) {
// Runnable readR = ()->read(lock);
Runnable readR = ()->read(readLock);
// Runnable writeR = ()->write(lock,new Random().nextInt());
Runnable writeR = ()->write(writeLock,new Random().nextInt());
for (int i = 0; i < 10; i++) new Thread(readR).start();
for (int i = 0; i < 2; i++) new Thread(writeR).start();
}
}
Semaphore
最多允许多少个线程同时运行
限流
Semaphore的参数代表当前最多有多少个线程能同时执行
import java.util.concurrent.Semaphore;
public class lock07 {
public static void main(String[] args) {
Semaphore s = new Semaphore(1); //系统灯 几个信号灯 参数是1,当前时刻最多只能执行一个, 参数为2,当前最多两个线程同时执行
//Semaphore s = new Semaphore(2,true);//设置为公平锁
new Thread(()->{
try {
s.acquire();//阻塞方法, 信号灯是1 ,s.acquire()之后信号灯变成0, 别的线程就无法acquire
System.out.println("t1 running");
Thread.sleep(200);
System.out.println("t1 running");
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
s.release(); //又把信号灯从0变为1
}
}).start();
new Thread(()->{
try {
s.acquire();
System.out.println("t2 running");
Thread.sleep(200);
System.out.println("t2 running");
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
s.release();
}
}).start();
}
}
Exchanger
exchanger()方法 阻塞
只能是两个线程之间
import java.util.concurrent.Exchanger;
public class lock08 {
static Exchanger<String> exchanger = new Exchanger<>();
public static void main(String[] args) {
new Thread(()->{
String s = "T1";
try {
s = exchanger.exchange(s);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" "+ s);
},"t1").start();
new Thread(()->{
String s = "T2";
try {
s = exchanger.exchange(s);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" "+ s);
},"t2").start();
}
}
t1线程的字符串T1被交换器阻塞, 等待另一个线程t2的字符串T2来交换. 如果没有另一个线程来进行交换, 那么t1线程会一直等待下去.
LockSupport
locksupport就好比wait方法, 但又不需要像syn那样上锁
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
public class lock09 {
public static void main(String[] args) {
Thread t = new Thread(()->{
for(int i =0;i<10;i++) {
System.out.println(i);
if(i == 5) {
LockSupport.park(); //当前线程阻塞 关门
}
try {
TimeUnit.SECONDS.sleep(1);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
// LockSupport.unpark(t); //开门, park不用等待
try {
TimeUnit.SECONDS.sleep(8);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("after 8 senconds!");
LockSupport.unpark(t); //指定唤醒某个阻塞的线程 unpark可以先于park调用 part启动比较靠后, unpart在线程启动后直接启动时,park是没效果的
}
}