JUC并发编程(一)
一. 什么是JUC
java.util.concurrent
java.util.concurrent.atomic
java.util.concurrent.locks
同属于Java.util 工具包
在业务中:用普通的线程代码 Thread(普通的线程类) Runnable 没有返回值、效率相比入 Callable相对较低! 所以,企业中Runnable用的比较少,更多的是Callable
之前学的callable 与lock都是 java.util 包下的
二. 进程和线程回顾
进程:一个程序,QQ.exe 等等程序的集合 一个进程往往包含多个线程,至少包含一个! Java默认有多少个线程? 两个:main线程,GC线程 对于Java而言,用Runnable,Thread,Callable 来开启线程
Java真的可以开启线程吗? 答案是:不能,本质上是调用本地方法(底层的C++),Java无法直接操作硬件
并发,并行
并发编程:并发,并行 并发:交替执行,一核cpu模拟出来多个线程,
并行:一起执行,多核cpu,多个线程可以同时执行,线程池
public class Demo01 {
public static void main(String[] args) {
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
---------------结果---------------
8
并发编程的本质:充分利用CPU资源
线程有几个状态
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
wait 和 sleep 的区别
- 来自不同的类
wait 属于Object类 sleep 属于Thread 类 - 关于锁的释放
wait会释放锁,sleep不会释放 - 使用范围不同
wait必须在同步代码块中使用 sleep在任何地方都能用
三. Lock锁(重点)
1. 传统Synchronized
public class Demo01 {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(()->{
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"c").start();
}
}
class Ticket{
private int number =50;
public synchronized void sale(){
if(number>0){
System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"票,剩余" +
number+"张票");
}
}
}
---------------结果---------------
A卖出了第50票,剩余49张票
A卖出了第49票,剩余48张票
A卖出了第48票,剩余47张票
。。。
B卖出了第3票,剩余2张票
A卖出了第2票,剩余1张票
A卖出了第1票,剩余0张票
2. Lock锁
lock锁中有三个接口
Condition 精确通知
标准Lock锁
ReadWriteLock 读写锁
lock锁中的两个用法:加锁/解锁 当我们打开ReentrantLock的源码中会发现:
默认定义了一个非公平锁 当传入一个布尔值为true则为公平锁 公平锁:非常公平,先来后到 非公平锁:可以插队(默认) Synchronized 默认也是非公平锁
用Lock锁重写卖票实例:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo02 {
public static void main(String[] args) {
Ticket2 ticket = new Ticket2();
new Thread(()->{
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"c").start();
}
}
class Ticket2{
private int number =50;
Lock lock = new ReentrantLock();
public void sale(){
lock.lock();
try {
if(number>0){
System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"票,剩余" +
number+"张票");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
---------------结果---------------
A卖出了第50票,剩余49张票
B卖出了第49票,剩余48张票
B卖出了第48票,剩余47张票
。。。
A卖出了第2票,剩余1张票
A卖出了第1票,剩余0张票
Synchronized 和 Lock锁的区别
- Synchronized 是Java内置的关键字,Lock是一个Java类
- Synchronized 无法判断获取锁的状态,Lock可以判断是否获取到了锁
- Synchronized 是自动的,会自动释放锁,Lock必须手动释放锁 容易发生死锁
- Synchronized 线程1(获得锁,阻塞) 线程2(等待,死等) ,Lock锁不一定会一直等待
- Synchronized 可重入锁,不可中断,非公平,Lock可重入,可以判断锁,可设置公平与否
- Synchronized 适合锁少量的代码同步问题,Lock锁适合锁大量的同步代码块
四. 生产者和消费者
1. Synchronized 版
public class Demo03 {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
class Data{
private int num=1;
public synchronized void increment() throws InterruptedException {
if (num!=0){
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName()+"=>"+num);
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
if (num==0){
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName()+"=>"+num);
this.notifyAll();
}
}
---------------结果---------------
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
问题:如果线程超过两个,会有什么问题? 答:会出现虚假唤醒问题 应该将if改成while,防止虚假唤醒问题
public class Demo03 {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Data{
private int num=1;
public synchronized void increment() throws InterruptedException {
while (num!=0){
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName()+"=>"+num);
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
while (num==0){
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName()+"=>"+num);
this.notifyAll();
}
}
---------------结果---------------
B=>0
A=>1
B=>0
C=>1
B=>0
A=>1
B=>0
C=>1
D=>0
A=>1
B=>0
C=>1
D=>0
A=>1
D=>0
C=>1
D=>0
A=>1
D=>0
C=>1
2.JUC版生产者消费者
要用Lock代替Synchronized ,就必须配套替换 wait 和 nodity 这就提到了之前的Condition,在JDK帮助文档中,明确介绍 代码实现:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo04 {
public static void main(String[] args) {
Data2 data2 = new Data2();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
data2.incerment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
data2.decerment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
data2.incerment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
data2.decerment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Data2{
private int num=1;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void incerment() throws InterruptedException {
lock.lock();
try {
while (num!=0){
condition.await();
}
num++;
System.out.println(Thread.currentThread().getName()+"=>"+num);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void decerment() throws InterruptedException {
lock.lock();
try {
while (num==0){
condition.await();
}
num--;
System.out.println(Thread.currentThread().getName()+"=>"+num);
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
---------------结果---------------
B=>0
A=>1
B=>0
C=>1
B=>0
C=>1
B=>0
C=>1
D=>0
C=>1
D=>0
C=>1
D=>0
A=>1
D=>0
A=>1
D=>0
A=>1
B=>0
A=>1
Condition的优点:精准通知和唤醒线程
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo05 {
public static void main(String[] args) {
Data3 data3 = new Data3();
new Thread(()->{
for (int i = 0; i < 5; i++) {
data3.printA();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
data3.printB();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
data3.printC();
}
},"C").start();
}
}
class Data3{
private int num=1;
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 (num!=1){
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"=>执行了PrintA");
num=2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while (num!=2){
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"=>执行了PrintB");
num=3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while (num!=3){
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"=>执行了PrintC");
num=1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
---------------结果---------------
A=>执行了PrintA
B=>执行了PrintB
C=>执行了PrintC
A=>执行了PrintA
B=>执行了PrintB
C=>执行了PrintC
A=>执行了PrintA
B=>执行了PrintB
C=>执行了PrintC
A=>执行了PrintA
B=>执行了PrintB
C=>执行了PrintC
A=>执行了PrintA
B=>执行了PrintB
C=>执行了PrintC
五. 8锁的现象
根据上面的实例,请问锁是什么,如何判断锁的是谁?
锁的内容:对象,与class模板
8锁,就是关于锁的8个问题 1、标准情况下,两个线程先打印发短信还是打电话?1/发短信2/打电话 2、 sendsms延迟4秒,两个线程先打印发短信还是打电话?1/发短信2/打电话
import java.util.concurrent.TimeUnit;
public class Demo06 {
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.call();
},"B").start();
}
}
class Phone{
public synchronized void sendSms(){
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
import java.util.concurrent.TimeUnit;
public class Demo06 {
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.call();
},"B").start();
}
}
class Phone{
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
import java.util.concurrent.TimeUnit;
public class Demo06 {
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.hello();
},"B").start();
}
}
class Phone{
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
public void hello(){
System.out.println("hello");
}
}
import java.util.concurrent.TimeUnit;
public class Demo06 {
public static void main(String[] args) {
Phone phone1 = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
phone1.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
class Phone{
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
import java.util.concurrent.TimeUnit;
public class Demo06 {
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.call();
},"B").start();
}
}
class Phone{
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
}
import java.util.concurrent.TimeUnit;
public class Demo06 {
public static void main(String[] args) {
Phone phone1 = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
phone1.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
class Phone{
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
}
import java.util.concurrent.TimeUnit;
public class Demo06 {
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.call();
},"B").start();
}
}
class Phone{
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
import java.util.concurrent.TimeUnit;
public class Demo06 {
public static void main(String[] args) {
Phone phone1 = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
phone1.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
class Phone{
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
答案: 1、发短信 打电话 2、发短信 打电话 原因:synchronized锁的对象是方法的调用者! 两个方法用的是同一个锁(phone),谁先拿到锁谁先执行 3、hello 发短信 hello没有锁,不是同步方法,不受锁的影响 4、打电话 发短信 两个对象,两个调用者,两个锁,没有冲突 5、发短信 打电话 Phone是唯一的一个CLass对象,类一加载就有了,锁的是Class 6、发短信 打电话 两个对象的Class类模板只有一个,锁的是Class 7、打电话 发短信 8、打电话 发短信 两个锁 锁的内容不一样,没有冲突
六. 集合类不安全
1.List不安全
实例一:
import java.util.Arrays;
import java.util.List;
public class ListTest {
public static void main(String[] args) {
List<String> list = Arrays.asList("1", "2", "3", "4");
list.forEach(System.out::println);
}
}
结果:
1
2
3
4
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class ListTest {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
for (int i = 1; i < 11; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,10));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
2.Set不安全
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class SetTest {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
for (int i = 1; i < 10; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
HashSet的本质是什么
public HashSet() {
map = new HashMap<>();
}
add set本质就是 map ,key是无法重复的
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
Map不安全
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class MapTest {
public static void main(String[] args) {
Map<String, Object> map = new HashMap<>();
for (int i=1;i<11;i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
ConcurrentHashMap的原理:
|