JUC编程
1、线程和进程
线程、进程
进程:一个可运行的程序。
一个进程可以包含多个线程,至少包含一个线程。
java默认的线程有2个:mian主线程、gc垃圾回收线程
线程:进程中的一个执行任务。
java不能开启线程,而是通过本地方法native,java没有权限开线程
并发、并行
并发编程:并发、并行
并发:多线程操作同一个资源
- cpu一核(一瞬间只能处理一个东西),模拟出来多条线程,通过快速交替,来实现多条线程(其实这是一种假象)
并行:意思就是多个人一起行走
- cpu多核:多个线程可以同时执行;可以使用线程池操作
查看cpu核数的代码:
system.out.println(Runtime/getRuntime().availableProcessors())
并发编程的本质:充分利用cpu的资源
线程有几个状态
new 新生
Runnable 运行
BLOCKED 阻塞
WAITING 等待,死等
TUMED_WAITING 超时等待
TERMINATED 终止
wait/sleep 区别
1、来自不同的类
wait=>object
sleep=>Thread
2、关于锁的释放
wait会释放锁
sleep不会释放(抱着锁睡觉了,就不会释放了)
3、使用的范围是不同的
wait:必须在同步代码块中
sleep:可以在任何地方睡
4、是否需要捕获异常
wait不需要捕获异常
sleep必须捕获异常
2、lock锁(重点)
传统synchronized
package com.xiao.demo01;
//基本的卖票例子
/**
*真正的多线程开发公司中的开发,降低偶合性
* 线程就是一个单独的资源类,没有任何附属的操作
* 1.属性,方法
*/
public class SaleTicketDemo01 {
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();
}
}
//资源类 oop面向对象
class Ticket{
//属性,方法
private int number = 50;
//卖票的方式
public synchronized void sale(){
if (number > 0){
System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余"+number);
}
}
}
lock接口
package com.xiao.demo01;
import jdk.nashorn.internal.ir.Block;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SaleTicketDemo02 {
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();
}
}
//lock三部曲
//1、new ReentrantLock();
//2、 lock.lock(); 加锁
//3、lock.unlock(); 解锁
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();
}
}
}
公平锁:十分公平,可以先来后到
非公平锁:十分不公平,可以插队(默认非公平锁)
synchronized和lock区别
- s ynchronized 内置的java关键字,lock是一个java类
- synchronized 无法判断获取锁的状态,lock可以判断是否获得了锁
- synchronized 会自动释放锁,lock必须要手动解锁,如果不释放锁,就会死锁
- synchronzied线程1(获得锁,阻塞)、线程2(等待,傻傻的等);lock锁就不一定会等待下去;try尝试
- synchronized可重入锁,不可以终端的,非公平;lock,可重入锁,可以判断锁,非公平(可以自己设置)
- synchronized适合锁少量的代码同步问题;lock锁大量的同步代码。
锁是什么,如何判断锁的是谁?
3、生产者和消费者问题
生产者和消费者问题 synchronized版 wait notify
package com.xiao.demo01;
public class SaleTicketDemo03 {
public static void main(String[] args) {
A a = new A();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
a.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"Q").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
a.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"W").start();
}
}
class A{
private int number = 0;
public synchronized void increment () throws InterruptedException {
if (number!=0){
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
this.notifyAll();
}
public synchronized void decrement () throws InterruptedException {
if (number==0){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
this.notifyAll();
}
}
问题存在:四个线程的情况下这么写还安全吗?
解决方法:将a类中的if判断改为while判断,防止虚假唤醒
package com.xiao.demo01;
public class SaleTicketDemo03 {
public static void main(String[] args) {
A a = new A();
new Thread(()->{
for (int i = 0; i < 40; i++) {
try {
a.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"Q").start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
try {
a.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"W").start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
try {
a.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"e").start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
try {
a.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"r").start();
}
}
class A{
private int number = 0;
public synchronized void increment () throws InterruptedException {
while (number!=0){
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
this.notifyAll();
}
public synchronized void decrement () throws InterruptedException {
while (number==0){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
this.notifyAll();
}
}
juc版的生产者和消费者问题 lock await signal
package com.xiao.demo01;
import jdk.nashorn.internal.ir.Block;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SaleTicketDemo03 {
public static void main(String[] args) {
A a = new A();
new Thread(()->{
for (int i = 0; i < 40; i++) {
try {
a.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"Q").start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
try {
a.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"W").start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
try {
a.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"e").start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
try {
a.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"r").start();
}
}
class A{
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increment () throws InterruptedException {
lock.lock();
try {
while (number!=0){
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement () throws InterruptedException {
lock.lock();
try {
while (number==0){
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
任何一个新的技术,绝对不是仅仅只有覆盖了原来的技术,优势和补充
Condition 精准的通知和唤醒线程
package com.xiao.demo01;
/**
* a执行完调用b,b执行完调用c,c执行完调用a
*/
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class C {
public static void main(String[] args) {
Data data = new Data();
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 Data {
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int number = 1;//1a 2b 3c
public void printA(){
lock.lock();
try {
//业务,判断 执行 通知
while (number!=1){
//等待
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"aaaaa");
//唤醒,唤醒指定的人b
number = 2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while (number!=2){
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"bbbbb");
number=3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while (number!=3){
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"ccccc");
number=1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
4、8锁现象
如何判断锁的是谁,永远的知道什么是锁,锁到底锁的是谁。
package com.xiao.demo01;
import java.util.concurrent.TimeUnit;
/**
* 8锁,就是关于锁的8个问题
* 1、标准情况下,两个线程先打印发短信还是打电话 发短信。打电话
*/
public class Test {
public static void main(String[] args) {
Prone prone = new Prone();
new Thread(()->{ prone.sendSms();},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{prone.call();},"B").start();
}
}
class Prone{
//synchronized锁的对象是方法的调用者
//两个方法用的是同一个锁,谁先拿到就谁执行
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
package test;
import java.util.concurrent.TimeUnit;
/**
* 3、增加了一个普通方法后,先执行发短信还是hello 普通方法
* 4、两个对象,两个同步方法,发短信还是打电话? 打电话
*/
public class Test2 {
public static void main(String[] args) {
//两个对象
Prone2 prone1 = new Prone2();
Prone2 prone2 = new Prone2();
new Thread(()->{prone1.sendSms();},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{prone2.call();},"B").start();
}
}
class Prone2{
//synchronized锁的对象是方法的调用者
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");
}
}
package test;
import java.util.concurrent.TimeUnit;
/**
* 7、一个静态的同步方法,一个普通的同步方法,一个对象 先哪个?
* 8、一个静态的同步方法,一个普通的同步方法,两个个对象 先哪个?
*/
public class Test4 {
public static void main(String[] args) {
//两个对象
Prone4 prone1 = new Prone4();
Prone4 prone2 = new Prone4();
new Thread(()->{prone1.sendSms();},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{prone2.call();},"B").start();
}
}
//phone3唯一的一个class对象
class Prone4{
//静态同步方法 锁的class模版
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("打电话");
}
}
小结
new this具体的一个手机
static class唯一的一个模版
5、集合类不安全
list不安全
package com.xiao.demo01;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
//java.util.ConcurrentModificationException 并发修改异常
public class ListTest {
public static void main(String[] args) {
//并发下 ArrayList 不安全的
/**
* 解决方案
* 1、Vector解决
* 2、List<string> list = Collections.synchronizedList(new ArrayList<>());
* 3、List<string> list = new CopyOnWriteArrayList<>();
*/
//CopyOnWrite 写入时复制 cow 计算机程序设计领域的一种优化策略
//多个线程使用的时候,list,读取的时候,固定的,写入,覆盖
//在写入的时候避免覆盖,造成数据问题
//读写分离
//CopyOnWrite比Vector好在前一个不用synchronized,效率相比就会更快
List<string> list = new CopyOnWriteArrayList<>();
for (int i = 1; i < 10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
set不安全
package com.xiao.demo01;import java.util.Collections;import java.util.HashSet;import java.util.Set;import java.util.UUID;import java.util.concurrent.CopyOnWriteArraySet;/** * 同理可证 java.util.ConcurrentModificationException * 1、Set<string> set = Collections.synchronizedSet(new HashSet<>()); * 2、Set<string> set = new CopyOnWriteArraySet<>(); */public class SetTest { public static void main(String[] args) { // Set<string> set = new HashSet<>(); // Set<string> set = Collections.synchronizedSet(new HashSet<>()); Set<string> set = new CopyOnWriteArraySet<>(); for (int i = 1; i < 30; i++) { new Thread(()->{ set.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(set); },String.valueOf(i)).start(); } }}
hashSet底层是什么? (就是hashmap)
public HashSet() { map = new HashMap<>();}//add set本质就是map key是无妨重复的public boolean add(E e) { return map.put(e, PRESENT)==null;}
6、Callable(简单)
1、可以有返回值
2、可以抛出异常
3、方法不同run()/call()
代码测试
callable通过runable的实现类futuretask来沟通到thread
package com.xiao.demo01;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// new Thread(new Runnable).start();
// new Thread().start(); //怎么启动Callable
MyThread thread = new MyThread();
FutureTask futureTask = new FutureTask(thread);
//适配类
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start();//结果会缓存,效率高
Integer o = (Integer) futureTask.get();//获取callable的返回结果
// 这个get方法可能产生堵塞,把它放到最后
//使用异步
System.out.println(o);
}
}
class MyThread implements Callable<integer>{
@Override
public Integer call() {
System.out.println("call()");
return 1024;
}
}
7、常用的辅助类
7.1、CountDownLatch
package com.xiao.demo01;import java.util.concurrent.CountDownLatch;public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { //总数是6 CountDownLatch countDownLatch = new CountDownLatch(6); for (int i = 1; i <= 6; i++) { new Thread(()->{ System.out.println(Thread.currentThread().getName()+"go out"); countDownLatch.countDown(); },String.valueOf(i)).start(); } countDownLatch.await();//等待计数器归零,然后再向下执行 System.out.println("Close Door"); }}
原理:
countDownLatch.countDown();数量-1
countDownLatch.await();//等待计数器归零,然后再向下执行
每次有线程调用countdown()数量-1,假设计数器为0,countDownLatch.await();就会被唤醒,继续执行
7.1、CyclicBarrier
package com.xiao.demo01;import java.util.concurrent.BrokenBarrierException;import java.util.concurrent.CyclicBarrier;public class CyclicBarrierDemo { public static void main(String[] args) { /** * 集齐7颗龙珠召唤神龙 * */ //召唤龙珠的线程 CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{ System.out.println("召唤神龙成功"); }); for (int i = 0; i < 7; i++) { final int temp = i; //Lambda 能操作到i吗? new Thread(()->{ System.out.println(Thread.currentThread().getName()+"收集"+temp+"龙珠"); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }).start(); } }}
7.3、Semaphore
package com.xiao.demo01;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <=6 ; i++) {
new Thread(()->{
//acquire得到
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 {
//release释放
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
原理:
acquire得到
,假设如果已经满了,等待,等待被释放为止
release释放,会将当前的信号量释放-1,然后唤醒等待的线程
作用:多个共享资源互斥的使用,并发限流,控制最大的线程数
8、读写锁
ReadWriteLock
read读可以被多线程同时读
Write写的时候只能有一个线程去写
package com.xiao.demo01;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 独占锁(写锁)一次只能被一个线程占有
* 共享锁(读锁)多个线程可以同时占有
* ReadWriteLock
* 读-读 可以共存
* 读-写 不能共存
* 写-写 不能共存
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCacheLock myCache = new MyCacheLock();
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 MyCacheLock{
private volatile Map<string,object> map = new HashMap<>();
//读写锁,更加细粒度的控制
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//存,写,写入的时候只希望同时只有一个线程写
public void put(String key,Object value){
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"写入"+key);
map.put(key, value);
System.out.println(Thread.currentThread().getName()+"写入OK");
} 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);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName()+"读取OK");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
/**
* 自定义缓存
*/
class MyCache{
private volatile Map<string,object> map = new HashMap<>();
//存,写
public void put(String key,Object value){
System.out.println(Thread.currentThread().getName()+"写入"+key);
map.put(key, value);
System.out.println(Thread.currentThread().getName()+"写入OK");
}
//取,读
public void get(String key){
System.out.println(Thread.currentThread().getName()+"读取"+key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName()+"读取OK");
}
}
</string,object></string,object>