JUC全称就是 Java.util.concurrent,我们去jdk1.8帮助文档看看
进程:是应用程序的启动实例,进程拥有打开代码和打开的文件资源、数据资源和独立的内存空间,它是资源分配的基本单位
线程:它是程序的实际执行者,一个进程可以有多个线程,但至少有一个,线程拥有自己的栈空间,它是调度分配的基本单位
协程:它是一种更轻量级的存在,正如进程有多个线程,线程也有多个协程
Java默认有几个线程? 2个:Main线程、GC线程
Java真的能开启线程吗? 不能,其实Java调用的start()方法底层又是去调用一个start0()的本地方法去开启的线程,Java是没有权限开启线程的
理解并行和并发的概念
这个概念是混的,从操作系统角度来讲:线程拥有5种状态,分别是:新建、可执行、运行、阻塞、死亡;从Java角度来讲:进入底层代码发现,它有6个枚举状态,分别是:NEW(新建)、RUNNABLE(运行)、WAITING(等待,傻傻的等)、TIMED_WAITING(超时等待,有时间)、BLOCKED(阻塞)、TERMINATED(终止)
wait | sleep | |
---|---|---|
1、来自不同的类 | Object | Thread |
2、关于锁的释放 | 释放锁(进入等待锁池,等待notify的唤醒) | 不释放锁(可以理解为抱着锁睡觉,一段时间和可以醒来继续执行) |
3、使用范围不同 | 必须在同步代码块中 | 可以在任何地方睡 |
4、方法属性 | 实例方法 | 静态方法 |
我们先看下jdk下的lock包下有什么实现类
锁从获取资源的公平性角度来讲可分为公平锁和非公平锁,从乐观和悲观方面分为乐观锁和悲观锁,从是否共享资源角度来讲可分为共享锁和独占锁,从锁的状态角度分为偏向锁、轻量级锁和重量级锁
线程间是如何通信的呢?最经典的例子就是生产者/消费者问题
有个小插曲 if和where 如果资源中使用if判断的话,可能会出现虚假唤醒的情况
package com.markus.pc;
/**
* Author:markusZhang
* degree of proficiency:
* Date:Create in 2020/5/26 21:55
*/
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i=0;i<10;i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i=0;i<10;i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i=0;i<10;i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i=0;i<10;i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Data{
private int number = 0;
public synchronized void increment() throws InterruptedException {
while(number!=0){
//等待
this.wait();
}
//业务逻辑
number++;
System.out.println(Thread.currentThread().getName()+"->"+number);
//通知其他线程我+1完毕了
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
while(number==0){
//等待
this.wait();
}
//业务逻辑
number--;
System.out.println(Thread.currentThread().getName()+"->"+number);
//唤醒其他线程
this.notifyAll();
}
}
package com.markus.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Author:markusZhang
* degree of proficiency:
* Date:Create in 2020/5/27 9:12
*/
public class B {
public static void main(String[] args) {
Data1 data = new Data1();
new Thread(()->{
for (int i=0;i<10;i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i=0;i<10;i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i=0;i<10;i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i=0;i<10;i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Data1{
private int number = 0;
private Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();//精准的通知和唤醒线程
public void increment() throws InterruptedException {
lock.lock();
try {
if(number!=0){
//等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"->"+number);
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try{
if(number==0){
//等待
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"->"+number);
condition.signalAll();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
syn的替代方案
syn+wait+notify完全可以用Lock+await()+signalAll()来替换,但技术的更新肯定不是只是替换的作用,还会与改进,就比如 Condition 可以来实现精准唤醒
package com.markus.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Author:markusZhang
* degree of proficiency:
* Date:Create in 2020/5/27 9:37
*/
public class C {
public static void main(String[] args) {
Data3 data = new Data3();
new Thread(()->{
for (int i = 0; i < 10 ; i++) {
data.printA();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10 ; i++) {
data.printB();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10 ; i++) {
data.printC();
}
},"C").start();
}
}
class Data3{
private int number = 1; //1A执行 2B 3C
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
public void printA(){
lock.lock();
try{
while(number!=1){
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"-> A");
number = 2;
condition2.signal();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try{
while(number!=2){
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"-> B");
number = 3;
condition3.signal();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try{
while(number!=3){
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"-> C");
number = 1;
condition1.signal();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
希望通过这几个例子,能让大家明白,锁的是谁
package com.markus.lock8;
import java.util.concurrent.TimeUnit;
/**
* Author:markusZhang
* degree of proficiency:
* Date:Create in 2020/5/27 22:25
* todo: 1、两个线程访问同一个对象的同一方法 (会发生互斥)
*/
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sendSMS();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.sendSMS();
},"B").start();
}
}
class Phone{
public synchronized void sendSMS(){
try {
System.out.println(Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信: "+Thread.currentThread().getName());
}
}
package com.markus.lock8;
import java.util.concurrent.TimeUnit;
/**
* Author:markusZhang
* degree of proficiency:
* Date:Create in 2020/5/27 22:33
* todo: 2、两个线程访问两个对象的同一方法 (不会发生互斥)
*/
public class Test2 {
public static void main(String[] args) {
Phone1 phone1 = new Phone1();
Phone1 phone2 = new Phone1();
new Thread(()->{
phone1.sendSMS();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.sendSMS();
},"B").start();
}
}
class Phone1{
public synchronized void sendSMS(){
try {
System.out.println(Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信: "+Thread.currentThread().getName());
}
}
package com.markus.lock8;
import java.util.concurrent.TimeUnit;
/**
* Author:markusZhang
* degree of proficiency:
* Date:Create in 2020/5/27 22:42
* todo: 3、两个线程访问synchronized修饰的静态方法 (会发生互斥)
*/
public class Test3 {
public static void main(String[] args) {
Phone2 phone1 = new Phone2();
Phone2 phone2 = new Phone2();
new Thread(()->{
phone1.sendSMS();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.sendSMS();
},"B").start();
}
}
class Phone2{
public static synchronized void sendSMS(){
try {
System.out.println(Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信: "+Thread.currentThread().getName());
}
}
package com.markus.lock8;
import java.util.concurrent.TimeUnit;
/**
* Author:markusZhang
* degree of proficiency:
* Date:Create in 2020/5/27 22:44
* todo: 4、两个线程访问同步方法和非同步方法 (不会互斥)
*/
public class Test4 {
public static void main(String[] args) {
Phone3 phone1 = new Phone3();
//Phone3 phone2 = new Phone3();
new Thread(()->{
phone1.sendSMS();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone1.hello();
},"B").start();
}
}
class Phone3{
public synchronized void sendSMS(){
try {
System.out.println(Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信: "+Thread.currentThread().getName());
}
public void hello(){
System.out.println(Thread.currentThread().getName());
System.out.println("hello: "+Thread.currentThread().getName());
}
}
package com.markus.lock8;
import java.util.concurrent.TimeUnit;
/**
* Author:markusZhang
* degree of proficiency:
* Date:Create in 2020/5/27 22:46
* todo: 5、两个线程访问同一个对象的不同的普通同步方法 (会发生互斥)
*/
public class Test5{
public static void main(String[] args) {
Phone4 phone1 = new Phone4();
//Phone3 phone2 = new Phone3();
new Thread(()->{
phone1.sendSMS();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone1.hello();
},"B").start();
}
}
class Phone4{
public synchronized void sendSMS(){
try {
System.out.println(Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信: "+Thread.currentThread().getName());
}
public synchronized void hello(){
System.out.println(Thread.currentThread().getName());
System.out.println("hello: "+Thread.currentThread().getName());
}
}
package com.markus.lock8;
import java.util.concurrent.TimeUnit;
/**
* Author:markusZhang
* degree of proficiency:
* Date:Create in 2020/5/27 22:48
* todo: 6、两个线程同时访问同一类下不同的静态synchronized方法 (互斥)
* 7、两个线程同时访问同一类下不同对对象的不同的静态synchronized方法 (互斥)
*/
public class Test6 {
public static void main(String[] args) {
Phone5 phone1 = new Phone5();
Phone5 phone2 = new Phone5();
new Thread(()->{
phone1.sendSMS();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.hello();
},"B").start();
}
}
class Phone5{
public static synchronized void sendSMS(){
try {
System.out.println(Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信: "+Thread.currentThread().getName());
}
public static synchronized void hello(){
System.out.println(Thread.currentThread().getName());
System.out.println("hello: "+Thread.currentThread().getName());
}
}
在单线程情况下,ArrayList是没有问题的,但是在多线程环境下,会出现异常:ConcurrentModificationException
package com.markus.unsafe;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Author:markusZhang
* degree of proficiency:
* Date:Create in 2020/5/27 23:02
*/
public class ListTest {
public static void main(String[] args) {
//并发情况下,ArrayList是线程不安全的,会出现ConcurrentModificationException异常
//我们如何去保证它线程安全呢?
//1、使用vector
//2、使用Collections.synchronizedList(list),封装,底层是维护的一个Object对象,对链表操作的时候对这个对象加锁
//3、使用J.U.C下的CopyOnWriteArrayList;
// List list = new ArrayList<>();
// List list = new Vector<>();
// List list = Collections.synchronizedList(new ArrayList<>());
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10 ; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
我们来看看CopyOnWriteArrayList底层,顾名思义:写时复制,读的时候不影响,当在写的时候创建一个副本去写,写完之后将旧数组指向新数组。它是实现了一个ReentrantLock对象来进行加锁解锁,然后维护了一个volatile的array对象数组
HashSet的底层其实就是一个HashMap,利用HashMap的key进行存值,它是线程不安全的
package com.markus.unsafe;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Author:markusZhang
* degree of proficiency:
* Date:Create in 2020/5/27 23:17
*/
public class SetTest {
public static void main(String[] args) {
//线程不安全 出现 ConcurrentModificationException
// 1、通过Collections的同步set来实现线程安全
// 2、通过 J.U.C下的CopyOnWriteArraySet来实现线程安全
// Set set = new HashSet<>();
// Set set = Collections.synchronizedSet(new HashSet<>());
Set<String> set = new CopyOnWriteArraySet<>();
for(int i = 0 ; i < 30 ; i++){
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
package com.markus.unsafe;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* Author:markusZhang
* degree of proficiency:
* Date:Create in 2020/5/28 9:21
*/
public class MapTest {
public static void main(String[] args) {
//并发情况 出现 ConcurrentModificationException
// 1、通过Collections.synchronizedMap();解决
// 2、通过ConcurrentHashMap解决
// Map map = new HashMap<>();
// Map map = Collections.synchronizedMap(new HashMap<>());
Map<String,String> map = new ConcurrentHashMap<>();
for (int i = 0; i < 30 ; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
1、有缓存
2、结果可能会等待
package com.markus.callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* Author:markusZhang
* degree of proficiency:
* Date:Create in 2020/5/28 9:47
*/
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//new Thread() 只认识 Runnable
//new Thread(Runnable) Runnable有一个实现类:FutureTask
//new Thread(FutureTask) FutureTask认识Callable
MyThread thread = new MyThread();
FutureTask futureTask = new FutureTask(thread);
new Thread(futureTask).start();
new Thread(futureTask).start();//按常理说它应该执行两次call,但是实际执行只有一次,结果会被缓存,提高效率
Integer i = (Integer) futureTask.get();//它会阻塞,如果call方法是一个耗时操作,它会一直阻塞到返回返回值
//可采用异步通信的方式解决
System.out.println(i);
}
}
class MyThread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("call()");
return 1024;
}
}
package com.markus.add;
import java.util.concurrent.CountDownLatch;
/**
* Author:markusZhang
* degree of proficiency:
* Date:Create in 2020/5/28 10:36
*/
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 6 ; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"Go out");
countDownLatch.countDown();// -1 操作
},String.valueOf(i)).start();
}
countDownLatch.await();//当计数器为0时,被唤醒,继续向下执行
System.out.println("Close Door");
}
}
package com.markus.add;
import com.markus.pc.C;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* Author:markusZhang
* degree of proficiency:
* Date:Create in 2020/5/28 12:04
*/
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(3,()->{
System.out.println("西天取经成功");
});
for (int i = 1; i <= 3; i++) {
final int temp = i;
// lambda能操作到 i 吗?
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+"收徒"+temp+"个徒弟");
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
原理:
semaphore.acquire():获得,如果已经满了,就需要等待,直到被释放为止
semaphore.release():释放,会将当前的信号量释放 +1,然后唤醒等待的线程
作用:多个共享资源的互斥的使用!并发限流,控制最大线程数
package com.markus.add;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* Author:markusZhang
* degree of proficiency:
* Date:Create in 2020/5/28 13:52
*/
public class SemaphoreDemo {
public static void main(String[] args) {
//模拟 停车位
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 6 ; i++) {
new Thread(()->{
try {
semaphore.acquire();//获取车位
System.out.println(Thread.currentThread().getName()+"得到车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();//释放
}
},String.valueOf(i)).start();
}
}
}
我们先去看下jdk1.8帮助文档
来看看官方的解读
它关联一对lock,读锁(共享锁)和写锁(独占锁),当进行读取数据的时候,可以允许多个线程进行读取,而进行写数据的时候,只能有一个线程占有资源。根据他的实现类的字面意思也就是它是可重入的锁
代码案例:
package com.markus.rw;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Author:markusZhang
* degree of proficiency:
* Date:Create in 2020/5/28 14:56
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i = 1; i <= 5 ; i++) {
final int temp = i;
new Thread(()->{
myCache.put(temp+"",temp+"");
},String.valueOf(i)).start();
}
for (int i = 1; i <= 5 ; i++) {
final int temp = i;
new Thread(()->{
myCache.get(temp+"");
},String.valueOf(i)).start();
}
}
}
class MyCache{
private volatile Map<String,String> map = new HashMap<>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void put(String key,String value){
readWriteLock.writeLock().lock();
try{
System.out.println(Thread.currentThread().getName()+"写入"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入成功");
}catch (Exception e){
e.printStackTrace();
}finally {
readWriteLock.writeLock().unlock();
}
}
public void get(String key){
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"读取"+key);
map.get(key);
System.out.println(Thread.currentThread().getName()+"读取成功");
}catch (Exception e){
e.printStackTrace();
}finally {
readWriteLock.readLock().unlock();
}
}
}