2021-02-07
完结篇,建议先看我的第一篇多线程高并发~~
1.CPU轮询调度,哪个线程获得CPU时间片,获得CPU时间片的线程执行。主线程main,其他线程
并行:多核cpu,同一时刻多个线程一起执行 并发:在同一个核心,cpu在多个线程之间切换,看哪个线程获取了cpu的时间片则执行。
2.TimeUnit.MILLISECONDS.sleep(1000); TimeUnit.SECONDS.sleep(1); 睡眠1s
3.创建线程3种方式:继承Thread类重写run()方法,实现Runable接口重写run()方法,线程池(Executors线程池工厂)
public class Thread02 {
public static void main(String[] args) {
Thread t = new Thread(){
@Override
public void run() {
System.out.println("创建线程的第一种方式");
}
};
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("创建线程的第二种方式");
}
});
ExecutorService service = Executors.newFixedThreadPool(2);
for(int i = 0;i<5;i++){
int k = i;
service.submit(()->{
System.out.println("创建线程的第三种方式"+k);
});
}
service.shutdown();
t.start();
t2.start();
System.out.println("主线程");
}
}
3.Thread.sleep(), t.join(), Thread.yield()
sleep:线程睡眠,但是不会释放资源,睡眠时间到后线程接着运行,不会释放cpu时间片
t.join:等待t线程执行完后再继续执行
yield:线程让步,让出当前cpu,再和其他线程一起竞争cpu,可能下次还是这个线程获得cpu继续执行
public class Thread03 {
private static void testSleep(){
Thread t = new Thread(()->{
try {
System.out.println(System.currentTimeMillis());
Thread.sleep(10000);
System.out.println(System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t.start();
}
private static void testJoin(){
Thread t1 = new Thread(()->{
for(int i=0;i<30;i++){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" "+i);
}
},"t1");
Thread t2 = new Thread(()->{
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=0;i<30;i++){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" "+i);
}
},"t2");
t1.start();
t2.start();
}
private static void testYield(){
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<10;i++){
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" "+i);
Thread.yield();
}
}
},"t1");
Thread t2= new Thread(()->{
for(int i=0;i<10;i++){
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" "+i);
Thread.yield();
}
},"t2");
t1.start();
t2.start();
}
public static void main(String[] args) {
//testSleep();
//testJoin();
testYield();
}
}
4.线程6种状态
可以通过t.getState() 来获取当前线程的状态
New、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED
NEW:线程被创建出来还没有被调用start()时候的状态
RUNNABLE:当线程被调用了start(),且处于等待操作系统分配资源(如CPU)、正在运行状态,即表示Running状态和Ready
BLOCKED:进入synchronized块/方法,锁被其它线程占有,这个时候被操作系统挂起,状态为阻塞状态
WAITING:当线程调用wait()/join()/LockSupport.park()不加超时时间的方法之后所处的状态,当前状态的线程不会被分配CPU资源和持有锁
TIMED_WAITING:线程调用sleep(睡眠时间)/wait(等待时间)/join(等待时间)/ LockSupport.parkNanos(等待时间)/LockSupport.parkUntil(等待时间)方法之后所处的状态,在指定的时间没有被唤醒或者等待线程没有结束,会被系统自动唤醒,正常退出
TERMINATED:线程执行结束
5.工作空间:Java内存模型分为主内存,和工作内存。主内存是所有的线程所共享的,工作内存是每个线程自己有一个,不是共享的。每个线程从主内存把变量拷贝到自己的工作内存,修改完后再把变量写回到主内存。
线程安全:多个线程对同一个变量进行修改,得到不正确的结果,是线程不安全的。
6.同步代码块和同步方法:同一时刻只允许一个线程访问同步代码块和同步方法。获得对象的锁,获取本身所this,获取类所.class。
同步代码块和同步方法都有静态和非静态。非静态:同步方法等同于synchronized(this),静态的等同于synchronized(.class)
public class Thead05 {
private static int a=50;
private static void m(){
//静态同步代码块,获取类的锁
synchronized (Thead05.class){
a--;
System.out.println(Thread.currentThread().getName()+" "+a);
}
}
//静态同步方法
synchronized private static void m2(){
a--;
System.out.println(Thread.currentThread().getName()+" "+a);
}
public static void main(String[] args) {
for(int i=0;i<50;i++){
new Thread(()->{
m2(); //m();
}).start();
}
}
}
public class Thread04 {
private final Object o = new Object();
int a=10;
int b=10;
//等价于synchronized(this)
synchronized void m1(){
a--;
System.out.println(Thread.currentThread().getName()+" "+a);
}
void m2(){
synchronized (this){
b--;
System.out.println(Thread.currentThread().getName()+" "+b);
}
}
void m3(){
synchronized (o){
b--;
System.out.println(Thread.currentThread().getName()+" "+b);
}
}
void m4(){
synchronized (Thread04.class){
b--;
System.out.println(Thread.currentThread().getName()+" "+b);
}
}
public static void main(String[] args) {
Thread04 t04 = new Thread04();
for(int i = 0;i<5;i++){
int k = i;
new Thread(()->{
t04.m1();
},"线程"+k).start();
}
for(int i=0;i<5;i++){
new Thread(()->{
t04.m4();
}).start();
}
}
}
7.可重入锁:某个线程已经获得某个锁,可以再次获取相同的锁而不会出现死锁。synchronized和ReentrantLock都是可重入的
ReentrantLock 和 synchronized 不一样,需要手动释放锁,所以使用 ReentrantLock的时候一定要手动释放锁,并且加锁次数和释放次数要一样
public class T {
synchronized void m1() {
System.out.println("m1 start");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
m2();
System.out.println("m1 end");
}
synchronized void m2() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m2");
}
public static void main(String[] args) {
new T().m1();
}
}
8.synchronized抛出异常锁会被释放
9.synchronized底层实现原理:每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权。monitorexit 释放所有权。通过monitorenter和monitorexit 两条jvm指令来实现的。
- 线程解锁前,必须把共享变量的最新值刷新到主内存中
- 线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时,需要从主内存中重新读取最新的值(注意:加锁与解锁需要是同一把锁)
10.volitate:mesi(缓存一致性),禁止指令重排序。适用范围:对变量的写入操作不依赖其当前值
11.区别:synchronized既能够保证可见性,又能保证原子性,而volatile只能保证可见性,无法保证原子性。
10.锁升级:jdk早期重量级锁(系统级别),偏向锁->自旋锁(有竞争)->重量级锁(旋转10次)
11.采用细粒度的锁,可以使线程争用时间变短,从而提高效率。在需要的地方上锁,而不是整个方法上锁,提高线程执行效率
12.AtomicInteger,AtomicLong , AtomicBoolean, LongAdder 都是原子性操作,底层是CAS。LongAdder 比 AtomicLong 效率更高
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class Thread07 {
private AtomicInteger atomic = new AtomicInteger(0);
List<Thread> list = new LinkedList<Thread>();
public void m(){
for(int i=0;i<10000;i++){
atomic.incrementAndGet();
}
}
public static void main(String[] args) {
Thread07 t07 = new Thread07();
List<Thread> list = t07.list;
for(int i=0;i<100;i++){
list.add(new Thread(t07::m));
}
list.forEach((o)->{o.start();});
list.forEach((o)->{
try {
o.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(t07.atomic);
}
}
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
public class Thread08 {
private AtomicLong count = new AtomicLong();
private LongAdder count1 = new LongAdder();
public void m(){
for(int i=0;i<1000000;i++){
count.incrementAndGet();
}
}
public void m1(){
for(int i=0;i<1000000;i++){
count1.increment();
}
}
public static void main(String[] args) throws InterruptedException {
Thread08 t08 = new Thread08();
Thread[] arr = new Thread[100];
//测试AtomicLong
for(int i=0;i<arr.length;i++){
arr[i] = new Thread(t08::m);
}
long start = System.currentTimeMillis();
for(Thread t:arr){
t.start();
}
for(Thread t:arr){
t.join();
}
long end = System.currentTimeMillis();
System.out.println((end-start)+" "+t08.count); // 2099 100000000
//测试LongAdder
for(int i=0;i<arr.length;i++){
arr[i] = new Thread(t08::m1);
}
long start1 = System.currentTimeMillis();
for(Thread t:arr){
t.start();
}
for(Thread t:arr){
t.join();
}
long end1 = System.currentTimeMillis();
System.out.println((end1-start1)+" "+t08.count1); // 274 100000000
}
}
13.ReentrantLock :公平锁(true)非公平锁(false)默认非公平锁,可重入锁(加锁和解锁次数要一致)
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class Thread09 {
//true 为公平锁,false为非公平锁。在等待队列最前的线程获得锁为公平锁,否则为抢占式
private ReentrantLock reentrantLock = new ReentrantLock(false);
void m1(){
for(int i=0;i<26;i++){
try{
reentrantLock.lock(); //加锁
TimeUnit.MILLISECONDS.sleep(100);
System.out.print(i);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock(); //释放锁
}
}
}
void m2(){
char a = 'A';
for(int i=a;i<a+26;i++){
try{
reentrantLock.lock();
TimeUnit.MILLISECONDS.sleep(100);
System.out.print((char) i);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
}
public static void main(String[] args) {
Thread09 t09 = new Thread09();
Thread t = new Thread(t09::m1);
Thread t2 = new Thread(t09::m2);
t.start();
t2.start();
// 公平锁输出: A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q16R17S18T19U20V21W22X23Y24Z25
// 非公平锁:不是交替输出
}
}
lock.tryLock() 尝试获取锁
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class Thread10 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
new Thread(()->{
try {
lock.lock();
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}).start();
new Thread(()->{
boolean flag = false;
try{
//尝试去获得锁,不管得不得到锁,下面的代码仍然会继续执行
/**
* 使用tryLock进行尝试锁定,不管锁定与否,方法都将继续执行
* 可以根据tryLock的返回值来判定是否锁定
* 也可以指定tryLock的时间,由于tryLock(time)抛出异常,所以要注意unclock的处理,必须放到finally中
*/
flag = lock.tryLock(5,TimeUnit.SECONDS);
System.out.println("kkkkk");
System.out.println(flag);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//如果获得锁就释放锁
if(flag){
lock.unlock();
}
}
}).start();
}
}
14.CountDownLatch:相当于一个计数器,原子性操作。线程执行完毕后计数器递减,当计数器的值为0时,表示所有线程都执行完毕
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.LongAdder;
public class Thread11 {
public static void main(String[] args) {
Thread[] arr = new Thread[1000];
CountDownLatch countDownLatch = new CountDownLatch(arr.length); //构造方法
LongAdder adder = new LongAdder();
for(int i = 0;i<1000;i++){
arr[i] = new Thread(()->{
for(int k=0;k<100000;k++){
adder.increment();
}
countDownLatch.countDown(); //自减操作
});
}
for(Thread t:arr){
t.start();
}
try {
countDownLatch.await(); //阻塞,等待,直到计数器为0才继续执行
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(adder);
}
}
第二个例子
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
public class CountDownLatchDemo {
private AtomicLong atomicLong = new AtomicLong(0);
List<Thread> list = new ArrayList<Thread>();
public CountDownLatch countDownLatch = new CountDownLatch(100);
public void addList(Thread t){
list.add(t);
}
public List<Thread> getList(){
return list;
}
public AtomicLong getAtomicLong(){
return atomicLong;
}
public void incre(){
for(int k=0;k<10000;k++){
atomicLong.incrementAndGet();
}
countDownLatch.countDown();
}
public static void main(String[] args) throws InterruptedException {
CountDownLatchDemo countDownLatchDemo = new CountDownLatchDemo();
for(int i=0;i<100;i++){
/**
Thread t = new Thread(new Runnable() {
@Override
public void run() {
countDownLatchDemo.incre();
}
});
*/
Thread t = new Thread(countDownLatchDemo::incre);
countDownLatchDemo.addList(t);
}
countDownLatchDemo.getList().forEach((t)->{t.start();});
countDownLatchDemo.countDownLatch.await();
System.out.println(countDownLatchDemo.getAtomicLong()); //1000000 10000*100
}
}
15.CycliBarrier 相当于一个栅栏,人数凑齐之后栅栏才打开,再继续往下执行
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CycliBarrierDemo {
CyclicBarrier cyclicBarrier = new CyclicBarrier(5,()->{
System.out.println("CycliBarrier Test");
});
public static void main(String[] args) {
CycliBarrierDemo demo = new CycliBarrierDemo();
for(int i=0;i<20;i++){
new Thread(){
@Override
public void run() {
try {
//阻塞住,不往下执行,等到5个之后才继续往下执行
demo.cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}.start();
}
}
}
16.ReadWriteLock 读写锁,这是一个接口,其实现类为ReentrantReadWriteLock。有readlock 读锁和writelock写锁两把锁
读锁是共享锁,写锁是独占锁
获取读锁的条件:没有其他线程的写锁
获取写锁的条件:没有其他线程的读锁,没有其他线程的写锁
一个线程要想同时持有写锁和读锁,必须先获取写锁再获取读锁;写锁可以“降级”为读锁;读锁不能“升级”为写锁。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReentrantReadWriteLockDemo {
private static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//读锁
private static Lock readLock = readWriteLock.readLock();
//写锁
private static Lock writeLock = readWriteLock.writeLock();
private static void read() {
readLock.lock();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":获取了读锁");
//释放锁
readLock.unlock();
}
private static void write() {
writeLock.lock();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+": 获取了写锁");
writeLock.unlock();
}
/**
* 此例证明读锁是共享锁,多个线程可以同时获得读锁
* 写锁是独占锁,排它锁,一个时间内只有一个线程可以获得
* @param args
*/
public static void main(String[] args) {
for(int i = 0 ;i<20;i++){
new Thread(ReentrantReadWriteLockDemo::read).start();
}
for(int i=0;i<3;i++){
new Thread(ReentrantReadWriteLockDemo::write).start();
}
}
}
-
Semaphore 信号量,可以控制同时访问资源的线程个数,Semaphore的主要方法摘要:
void acquire() :从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。
void release():释放一个许可,将其返回给信号量。
int availablePermits():返回此信号量中当前可用的许可数。
import java.util.concurrent.Semaphore; public class SemaphoreDemo { public static void main(String[] args) { Semaphore semaphore = new Semaphore(2); Thread[] threads = new Thread[200]; for(int i=0;i<200;i++){ threads[i] = new Thread(()->{ try { /** * 没有获得信号量的线程阻塞等待,获得信号量信号量-- */ semaphore.acquire(); Thread.sleep(500); System.out.println(Thread.currentThread().getName()+":获得信号量"); } catch (InterruptedException e) { e.printStackTrace(); } //释放信号量,信号量++ semaphore.release(); }); } for(int i=0;i<threads.length;i++){ threads[i].start(); } } }
-
LockingSupport 程中首选看是否有许可,有许可就立马返回,而每次unpark都会给许可设置成有,这意味着,可以先执行unpark,给予许可,再执行park立马自行.
unpark(Thread thread)释放thread线程的许可证 park() 当前线程获取许可证
import java.util.concurrent.locks.LockSupport; public class LockingSupportDemo { private static Thread t1; private static Thread t2; public static void main(String[] args) { int num = 1; char letter = 'a'; t2 = new Thread(new Runnable() { @Override public void run() { for(char letter='a';letter<='z';letter++){ System.out.print(letter); LockSupport.unpark(t1); //释放许可 LockSupport.park(); } } }); t1 = new Thread(new Runnable() { @Override public void run() { for(int i = 1;i<=26;i++){ LockSupport.park(); //获取许可,如果没有许可则阻塞 System.out.print(i); LockSupport.unpark(t2); } } }); t1.start(); t2.start(); } }
-
synchronized 同步代码块,同步方法
/** * 两个线程顺序输出A1B2C3D4...26Z * synchronized 同步代码块,用来修饰的方法为同步方法 * 同一时刻只有一个线程可以获得锁,没有获得锁的线程进入阻塞状态 * 锁:对象的锁,类的锁 synchronized(A.class),同步方法=synchronized(this){} * wait(),notify(),nofityAll() 需要配合synchronized使用 * wait() 释放锁,线程进入阻塞状态,直到有notify 或者 notifyAll 唤醒,然后去竞争锁 * nofity() 唤醒在等待队列中最前面的线程,notifyAll() 唤醒在等待队列中的所有线程,然后去竞争这把锁 * */ public class WaitNotifyDemo { private Object object = new Object(); public void printNumber(int i){ System.out.print(i); } public void printLetter(char c){ System.out.print(c); } public static void main(String[] args) { WaitNotifyDemo wnd = new WaitNotifyDemo(); Object o = wnd.object; Thread t1 = new Thread(new Runnable() { @Override public void run() { for(int i=1;i<27;i++){ synchronized (o){ try { wnd.printNumber(i); o.notify(); o.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { for(char c = 'A'; c<='Z';c++){ synchronized (o){ try { wnd.printLetter(c); o.notify(); o.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }); t1.start(); t2.start(); } }
//例子2 public class WaitNofityDemo2 { private Object o = new Object(); public void printNumber(){ for(int i=1;i<27;i++){ synchronized (o){ try { System.out.print(i); o.notifyAll(); o.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } public void printLetter(){ for(char c='A';c<='Z';c++){ synchronized (o){ try { System.out.print(c); o.notifyAll(); o.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { WaitNofityDemo2 waitNofityDemo2 = new WaitNofityDemo2(); Thread t1 = new Thread(waitNofityDemo2::printNumber); Thread t2 = new Thread(waitNofityDemo2::printLetter); t1.start(); t2.start(); } }
-
ReentrantLock
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; /** * Condition 条件队列,signal() 唤醒条件队列最前的线程,去获取锁,signalAll() 唤醒条件队列的所有线程, * await() 释放锁,进入条件队列,需要signal() 或者signalAll() 唤醒 * 可以有多个condition 条件队列 * * ReentrantLock lock() 获取锁 unlock() 释放锁 */ public class ReentrantLockDemo { static ReentrantLock lock; static Condition condition; static{ lock = new ReentrantLock(); condition = lock.newCondition(); } public static void printNumber() { for(int i = 1;i<=26;i++){ try { lock.lock(); //获取锁 System.out.print(i); condition.signal(); condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } } lock.unlock(); //释放锁 } public static void printLetter() { for(char c = 'A';c<='Z';c++){ try { lock.lock(); //获取锁 System.out.print(c); condition.signal(); condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } } lock.unlock(); //释放锁 } public static void main(String[] args) { Thread t1 = new Thread(ReentrantLockDemo::printNumber); Thread t2 = new Thread(ReentrantLockDemo::printLetter); t1.start(); t2.start(); } }
-
强、软、弱、虚 四种引用
强:通过new 关键字创建的对象是强引用,jvm内存不足时候也不会对对象进行垃圾回收
软:jvm内存足够垃圾回收器不会回收,内存不足时,第一次垃圾回收还是内存不足则会回收软引用对象
弱:被垃圾扫描器扫描到就会回收,不管jvm内存是否足够
虚:在任何时候都可能被垃圾回收。 虚引用主要用来跟踪对象被垃圾回收的活动
-
ThreadLocal 不同线程变量隔离,同一个线程变量共享。set(),get() 方法
ThreadLocal有静态内部类ThreadLocalMap
Thread 类有成员变量 ThreadLocal.ThreadLocalMap,set()方法就是往自己的成员变量设置值,get()方法获取值
ThreadLocalMap的key是this,存储ThreadLocal对象,value是设置的值
public class ThreadLocalDemo { public static void main(String[] args) { ThreadLocal<String> threadLocal = new ThreadLocal<>(); Thread[] threads = new Thread[10]; for(int i = 0;i<threads.length;i++){ threads[i] = new Thread(()->{ threadLocal.set(Thread.currentThread().getName()); System.out.println(threadLocal.get()); }); } // for(int i = 0;i<threads.length;i++){ // threads[i].start(); // } for(Thread t:threads){ t.start(); } } } /** * 输出: * Thread-0 * Thread-2 * Thread-1 * Thread-4 * Thread-6 * Thread-5 * Thread-3 * Thread-9 * Thread-8 * Thread-7 */
-
顺序输出A1B2C3… 生产者消费者程序,售票程序
import java.util.ArrayList; import java.util.List; /** * 生产者消费者 */ public class ContainerDemo { private volatile static List<String> list = new ArrayList<String>(); private static Object o = new Object(); public static void main(String[] args) { new Thread(()->{ synchronized (o){ while(true){ if(list.size()<5){ list.add(Thread.currentThread().getName()); }else{ try { o.notifyAll(); o.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }).start(); new Thread(()->{ synchronized (o){ while(true){ if(list.size()>0){ System.out.println(list.remove(0)); }else{ try { o.notifyAll(); o.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }).start(); } }
import java.util.concurrent.atomic.AtomicInteger; /** * 售票程序 * CAS 保证原子性 */ public class SellTicketDemo { private static AtomicInteger atomicInteger = new AtomicInteger(10000); //cas 保证原子性 private static int m1; private static int m2; public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(()->{ while(atomicInteger.get()>1){ atomicInteger.decrementAndGet(); m1++; } }); Thread t2 = new Thread(()->{ while(atomicInteger.get()>0){ atomicInteger.decrementAndGet(); m2++; } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("t1:"+m1); System.out.println("t2:"+m2); System.out.println(m1+m2); } }
public class SellTicketDemo2 { /** *售票2 * */ private static int ticket = 1000000; private static int win1 = 0; private static int win2 = 0; public synchronized static void win1Sell(){ if(ticket>0){ ticket--; win1++; } } public synchronized static void win2Sell(){ if(ticket>0){ ticket--; win2++; } } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new Runnable() { @Override public void run() { while(ticket>0){ win1Sell(); } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { while(ticket>0){ win2Sell(); } } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("win1:"+ win1 + " win2:"+win2); System.out.println(win1+win2); } }
-
并发容器
并编程中,一般需要用到安全的队列,如果要自己实现安全队列,可以使用2种方式:
方式1:加锁,这种实现方式就是我们常说的阻塞队列。
方式2:使用循环CAS算法实现,这种方式实现队列称之为非阻塞队列。ArrayList:线程不安全 LinkedList:线程不安全 HashMap:线程不安全
PriorityQueue(线程不安全): 优先级队列。默认排序升序,可以自定义比较器Comparotor实现自定义排序
HashTable: 线程安全的,每个方法都是同步方法 synchronized 修饰.synchronized来保证线程安全,但在线程竞争激烈的情况下Hashtable的效率非常低下。因为当一个线程访问Hashtable的同步方法时,其他线程访问Hashtable的同步方法时,可能会进入阻塞或轮询状态。如线程1使用put进行添加元素,线程2不但不能使用put方法添加元素,并且也不能使用get方法来获取元素,所以竞争越激烈效率越低.3。(HashMap 线程不安全)
ConcurrentHashMap: 1.7 segment[] 数组+ HashEntry[] 数组,分段加锁的方式支持每个线程对一部分数据进行操作,加锁的方式是Reentrantlock。jdk1.8 抛弃分段加锁,采用cas+synchronized
CopyOnWriteArrayList:(写时候synchronized加锁,只有一个线程可以写)写时复制技术,实现读写分离。读的时候不加锁,速度非常快,写的时候会对原来的容器进行一份拷贝,在新的arraylist上修改,再把引用指向新的arraylist。
读操作是无锁的,性能较高。至于写操作,比如向容器中添加一个元素,则首先将当前容器复制一份,然后在新副本上执行写操作,结束之后再将原容器的引用指向新容器
优点:读写分离,读操作和写操作在不同的容器,读操作不加锁效率非常高。
缺点:每次执行写操作都要将原容器拷贝一份,数据量大时,对内存压力较大,可能会引起频繁GC;二是无法保证实时性,写和读分别作用在新老不同容器上,在写操作执行过程中,读不会阻塞但读取到的却是老容器的数据
Vector:线程安全的ArrayList,方法都用synchronized修饰
ConcurrentLinkedQueue: cas 保证线程安全
-
BlockingQueue 阻塞队列
add() :添加成功返回true,否则抛出异常
offer():添加成功返回true,失败返回false
put():如果队列满了会进入阻塞状态
poll(): 从队列中取值,没有值返回null
take():从队列中获取值,如果队列中没有值,线程会一直阻塞,直到队列中有值,并且该方法取得了该值
方法使用ReentrantLock 加锁,保证线程安全
ArrayBlockingQueue LinkedBlockingDeque DelayQueue: 时blockingQueue的一种,属于延时队列 SynchronousQueue: blockingQueue一种,put()方法阻塞,直到队列的元素被消费。如果队列为空take() 方法阻塞。
-
ConcurrentSkipListMap
-
CompletableFuture
- 异步任务结束时,会自动回调某个对象的方法;
- 异步任务出错时,会自动回调某个对象的方法;
- 主线程设置好回调后,不再关心异步任务的执行
异步任务,jdk1.8新特性。以前通过FutureTask的get()方法或者Future的get() 方法获取线程的执行结果会阻塞,通过轮询或者阻塞方式去获取结果,造成CPU资源浪费而且不能实时得到结果,有违背异步编程原则。
import java.io.IOException; import java.util.Random; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; public class T06_01_CompletableFuture { public static void main(String[] args) throws ExecutionException, InterruptedException { long start, end; /*start = System.currentTimeMillis(); priceOfTM(); priceOfTB(); priceOfJD(); end = System.currentTimeMillis(); System.out.println("use serial method call! " + (end - start));*/ start = System.currentTimeMillis(); CompletableFuture<Double> futureTM = CompletableFuture.supplyAsync(()->priceOfTM()); CompletableFuture<Double> futureTB = CompletableFuture.supplyAsync(()->priceOfTB()); CompletableFuture<Double> futureJD = CompletableFuture.supplyAsync(()->priceOfJD()); CompletableFuture.allOf(futureTM, futureTB, futureJD).join(); CompletableFuture.supplyAsync(()->priceOfTM()) .thenApply(String::valueOf) .thenApply(str-> "price " + str) .thenAccept(System.out::println); end = System.currentTimeMillis(); System.out.println("use completable future! " + (end - start)); try { System.in.read(); } catch (IOException e) { e.printStackTrace(); } } private static double priceOfTM() { delay(); return 1.00; } private static double priceOfTB() { delay(); return 2.00; } private static double priceOfJD() { delay(); return 3.00; } /*private static double priceOfAmazon() { delay(); throw new RuntimeException("product not exist!"); }*/ private static void delay() { int time = new Random().nextInt(500); try { TimeUnit.MILLISECONDS.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } System.out.printf("After %s sleep!\n", time); } }
- ForkJoinPool 用来执行ForkJoinTask,实现类有RecursiveTask(有返回值任务),RecursiveAction(无返回值任务),重写compute方法
- ForkJoinTask的fork()方法:添加到线程池异步执行此任务 join():等待返回结果,同步阻塞,也可以使用get(),同步阻塞
- 可以使用ForkJoinPool.execute(异步,不返回结果)/invoke(同步,返回结果)/submit(异步,返回结果)方法,来执行ForkJoinTask
-
线程池
线程池工厂:Executors
自定义线程池ThreadPoolExecutor
execute和submit 都是异步方法
newCachedThreadPool
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
newScheduledThreadPool
public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue()); }
newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
决绝策略 如果队列满了,线程也到达最大线程数,新执行拒绝策略
ThreadPoolExecutor.AbortPolicy() //丢弃任务,抛出异常 ThreadPoolExecutor.CallerRunsPolicy() //开启线程池的线程执行任务,缺点是可能会阻塞此线程 ThreadPoolExecutor.DiscardOldestPolicy() //将任务队列中时间最老的任务丢弃,将新任务添加入队列 ThreadPoolExecutor.DiscardPolicy() //将任务丢弃不做任何操作 //自定义拒绝策略 class Reject implements RejectedExecutionHandler{ @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { r.run(); } }