JUC并发编程01

1、JUC(Java.util.concurrent)

2、线程和进程

一个进程往往包含多个线程,至少包含一个;
java默认有两个线程? main;GC
线程:比进程更小的执行单位,
实现线程的三种方式:Thread,Runnable,Callale
Java开启不了多线程

并发和并行
如果多个线程能同时被多个cpu执行,就是并行。
如果多线程被一个cpu轮流着执行就是并发。
并发编程的本质:充分利用CPU的资源
线程的状态
六种状态:NEW、RUNNABLE、BLOCK、WAITING、TIMEWAITING、TERMINATED
wait、sleep
1、来自不同的类:wait属于Object;sleep属于Thread
2、关于锁的释放:wait释放锁;sleep不释放锁
3、使用的范围是不同的:wait只能在同步代码块或同步控制方法中使用;sleep可以在任意位置使用
4、是否需要捕获异常。

3、LOCK锁

synchronized

/**
 - 线程就是一个单独的资源类,没有任何附属操作;
 */
public class SaleTicketDemo01 {
	public static void main(String[] args) {
		// 并发:多线程操作同一个资源类,把资源类丢入线程
		Ticket ticket = new Ticket();
		new Thread(() -> {for (int i = 0; i < 20; i++) ticket.saleTicket();}, "A").start();
		new Thread(() -> {for (int i = 0; i < 20; i++) ticket.saleTicket();}, "B").start();
		new Thread(() -> {for (int i = 0; i < 20; i++) ticket.saleTicket();}, "C").start();
}
//资源类:属性、方法
class Ticket {
	private int number = 1;
	public synchronized void saleTicket() {
		if (number <= 50) {
	System.out.println(Thread.currentThread().getName() + "卖了第" + number + "张票" + "还剩" + (50 - number) + "张票");
			number ++;
		}
	}
}

lock

public class SaleTicketDemo2 {
	public static void main(String[] args) {
		// 并发:多线程操作同一个资源类,把资源类丢入线程
		Ticket01 ticket = new Ticket01();
		new Thread(() -> {for (int i = 0; i < 20; i++) ticket.saleTicket();}, "A").start();
		new Thread(() -> {for (int i = 0; i < 20; i++) ticket.saleTicket();}, "B").start();
		new Thread(() -> {for (int i = 0; i < 20; i++) ticket.saleTicket();}, "C").start();
	}
}
//资源类:属性、方法
class Ticket01 {
	private int number = 1;
	Lock lock = new ReentrantLock();// 创建锁对象
	public void saleTicket() {
		lock.lock();// 加锁
		try {
			// 资源操作,业务代码
			if (number <= 50) {
				System.out.println(
						Thread.currentThread().getName() + "卖了第" + number + "张票" + "还剩" + (50 - number) + "张票");
				number++;
			}
		} finally {
			lock.unlock();// 释放锁
		}
	}
}

公平锁:谁先来是谁的
非公平锁:谁抢到是谁的(默认);默认是可重入锁
Synchronized和Lock的区别

  • 层面:synchronized属于JVM层面,java关键字;Lock食欲java类
  • 是否可以判断锁的状态??
  • 锁的释放:Synchronized会自动释放锁,遇到异常会释放锁;Lock需要手动释放,遇到异常容易造成死锁
  • 阻塞时的操作:Synchronizd会一直等待;lock.trylock可以选择不等待
  • 锁的类型:Synchronized是可重入的非公平锁;lock控制公平不公平
  • 适用场景:Synchronized适合少量代码同步问题,Lock适合锁大量的同步代码

4、生产者和消费者问题

面试:单例模式,排序,生产者和消费者,死锁

public class SaleTicket {
	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		new Thread(()->{for (int i = 0; i <= 2; i++) ticket.increment();},"A").start();;
		new Thread(()->{for (int i = 0; i <= 2; i++) ticket.decrement();},"B").start();
	}
}
// 等待,业务,通知
class Ticket{
	private int number = 0;
	public synchronized void increment(){
		if (number != 0) { // 符合条件进入等待
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		number ++;// 业务
		System.out.println(Thread.currentThread().getName()+"生产了====>"+number);
		this.notifyAll();// 通知其他线程
	}
	
	public synchronized void decrement(){
		if (number == 0) { // 符合条件进入等待,只会判断一次
			try {
				this.wait(); // 等待中,当被唤醒时,会直接向下执行,不会在判断number的值,
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		number --;// 业务
		System.out.println(Thread.currentThread().getName()+"消费了===>" + number);
		this.notifyAll(); // 通知其他线程
	}
}

多个生产者和消费者问题
防止虚假唤醒问题 判断条件时使用while (CAS)
什么是虚假唤醒:

JUC版的生产者和消费者问题
如何学习:
Lock , await ,signalAll
Synchronized,wait,notifyAll

/*简单实现*/
public class SaleTicket2 {
	public static void main(String[] args) {
		Ticket02 ticket = new Ticket02();
		new Thread(()->{for (int i = 0; i <= 2; i++) ticket.increment();},"A").start();
		new Thread(()->{for (int i = 0; i <= 2; i++) ticket.increment();},"B").start();
		new Thread(()->{for (int i = 0; i <= 2; i++) ticket.increment();},"C").start();
		new Thread(()->{for (int i = 0; i <= 2; i++) ticket.increment();},"D").start();
		new Thread(()->{for (int i = 0; i <= 2; i++) ticket.decrement();},"E").start();
	}
}
// 等待,业务,通知
class Ticket02{
	private int number = 0;
	Lock lock = new ReentrantLock();
	Condition condition = lock.newCondition();
	public void increment() {
		lock.lock();
		try {
			while (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() {
		lock.lock();
		try {
			while (number == 0) {
				condition.await();
			}
			number--;// 业务
	System.out.println(Thread.currentThread().getName() + "消费了===>" + number);
			condition.signalAll(); // 通知其他线程
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
}

任何一个新的技术,绝对不是仅仅只是覆盖了原有的技术
实现有序执行:精准的通知和唤醒线程 ABC线程顺序执行

public class SaleTicket2 {
	public static void main(String[] args) {
		Ticket02 ticket = new Ticket02();
		new Thread(()->{for (int i = 1; i <= 3; i++) ticket.printA();},"A").start();
		new Thread(()->{for (int i = 1; i <= 4; i++) ticket.printB();},"B").start();
		new Thread(()->{for (int i = 1; i <= 5; i++) ticket.printC();},"C").start();
	}
}

// 等待,业务,通知
class Ticket02{
	private Lock lock = new ReentrantLock();
	private int number = 1;// 标识位
	Condition conditionA = lock.newCondition();
	Condition conditionB = lock.newCondition();
	Condition conditionC = lock.newCondition();
	
	public void printA() {
		lock.lock();
		try {
			while (number!=1) {
				conditionA.await(); // 等待
			}
			number = 2;// 业务
			System.out.println(Thread.currentThread().getName());
			conditionB.signal();// 通知指定的线程
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	public void printB() {
		lock.lock();
		try {
			while (number!=2) {
				conditionB.await();//等待
			}
			number = 3;// 业务
			System.out.println(Thread.currentThread().getName());
			conditionC.signal(); // 通知指定的线程
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	public void printC() {
		lock.lock();
		try {
			while (number!=3) {
				conditionC.await();// 等待
			}
			number = 1;// 业务
			System.out.println(Thread.currentThread().getName());
			conditionA.signal(); // 通知指定的线程
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
}

5、八锁现象

1、synchronized的锁的对象是方法的调用者:同一个对象会相互干扰,两个方法互不干扰。
2、static静态方法的锁的是class:不管是不是同一对象都会干扰。
3、加锁静态方法和加锁普通方法的锁不一样,互不干扰
4、加锁静态方法和无锁普通方法互不干扰
5、加锁普通方法和无锁普通方法互不干扰

6、集合类不安全

扩展:快速失败、安全失败

ArrayList和HashSet
/*单线程下安全、多线程情况下会发生并发修改异常*/
List<String> asList = Arrays.asList("a","b","c");
asList.forEach(System.out::println)

解决多线程情况的List安全问题
1.解决方法一:

List<String> list = Collections.synchronizedList(new ArrayList<String>());
		for (int i = 0; i < 10; i++) {
			new Thread(()->{list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);}).start();
		}

2、解决方法二:

// 写入时复制,COW 计算机程序设计领域的一种优化策略;在写入的时候避免覆盖,造成数据问题;读写分离
// 使用的锁是lock,Vector是synchronized
List<String> list = new CopyOnWriteArrayList<String>();
		for (int i = 0; i < 10; i++) {
			new Thread(()->{list.add(UUID.randomUUID().toString().substring(0,5));
			System.out.println(list);}).start();
		}
HashMap

问题:初始容量16(1<<4),加载因子0.75f
Map<String,String> map = new HashMap<String,String>();// 相当于 new HashMap<String, String>(16,0.75)
?官方文档查看ConcurrentHashMap
1、解决方法一:

Map<Object, String> map = Collections.synchronizedMap(new HashMap<Object, String>());
		for (int i = 0; i < 100; i++) {
			new Thread(()->{map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
			System.out.println(map);}).start();
		}

2、解决方法二:

Map<Object, String> map = new ConcurrentHashMap<Object, String>();
		for (int i = 0; i < 100; i++) {
			new Thread(()->{map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
			System.out.println(map);}).start();
		}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值