线程的状态
public enum State {
// 新建
NEW,
// 运行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待
WAITING,
// 超时等待
TIMED_WAITING,
// 终止
TERMINATED;
}
Lock锁
传统的Synchronized
// 基本的买票例子
public class SaleTicketDemo01 {
// 真正的线程开发,都会降低耦合性
// 线程就是一个资源类(属性,方法),没有任何附属操作
public static void main(String[] args) {
// 并发 多个线程操作同一资源类
final Ticket ticket = new Ticket();
// FunctionInterface 函数式接口 lambda表达式
// (参数)->{方法}
new Thread(()->{
for (int i = 0; i < 400; i++) {
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 400; i++) {
ticket.sale();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 400; i++) {
ticket.sale();
}
},"C").start();
}
}
// 资源类
class Ticket{
private int number = 300; // 30张票
// synchronized 锁
public synchronized void sale(){
if(number > 0){
System.out.println(Thread.currentThread().getName() + "卖出了:"+ number-- + "张票,剩余票数:"+number);
}
}
}
Lock 锁
Lock l = …; l.lock(); try { // access the resource protected by this lock } finally { l.unlock(); }
当在不同范围内发生锁定和解锁时,必须注意确保在锁定时执行的所有代码由try-finally或try-catch保护,以确保在必要时释放锁定。
实现的接口
ReentrantLock 可重入锁
ReentrantReadWriteLock.ReadLock 读锁
ReentrantReadWriteLock.WriteLock 写锁
Lock三部曲
1. Lock lock = new ReentrantLock();
2. lock.lock(); // 加锁
3. lock.unlock(); // 解锁
package com.it;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author shkstart
* @create 2020-06-17 9:25
*/
public class SaleTicketDemo02 {
public static void main(String[] args) {
// 并发 多个线程操作同一资源类
Ticket2 ticket = new Ticket2();
// FunctionInterface 函数式接口 lambda表达式
// (参数)->{方法}
new Thread(()->{
for (int i = 0; i < 400; i++) {
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 400; i++) {
ticket.sale();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 400; i++) {
ticket.sale();
}
},"C").start();
}
}
// 资源类
class Ticket2{
private int number = 300; // 30张票
Lock lock = new ReentrantLock();
public void sale(){
try{
lock.lock(); // 加锁
if(number > 0){
System.out.println(Thread.currentThread().getName() + "卖出了:"+ number-- + "张票,剩余票数:"+number);
}
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock(); // 解锁
}
}
}
Lock和Synchronized区别
1、 Synchronized是Java内置的关键字,Lock是一个Java类
2、 Synchronized无法获取锁的状态,Lock可以判断是否获取到了锁
3、 Synchronized会自动释放锁,Lock需要手动释放(如果不释放会死锁)
4、 Synchronized的线程发生阻塞其余线程会一直等待,而Lock不一定会等待
5、 Synchronized 可重入锁,不可以中断,非公平。Lock 可重入锁,可以判断锁,非公共(可以自己设置)
Lock 设置 公平 | 非公平 锁操作
Lock lock = new ReentrantLock(boolean fair);
如果参数fair为true为公平锁 false为非公平锁
6、 Synchronzied 适合少量代码的同步问题,Lock适合大量代码的同步问题
生产者消费者
虚假唤醒
摘抄自:https://www.cnblogs.com/tqyysm/articles/9765667.html
举个例子,我们现在有一个生产者-消费者队列和三个线程。
1) 1号线程从队列中获取了一个元素,此时队列变为空。
2) 2号线程也想从队列中获取一个元素,但此时队列为空,2号线程便只能进入阻塞(cond.wait()),等待队列非空。
3) 这时,3号线程将一个元素入队,并调用cond.notify()唤醒条件变量。
4) 处于等待状态的2号线程接收到3号线程的唤醒信号,便准备解除阻塞状态,执行接下来的任务(获取队列中的元素)。
5) 然而可能出现这样的情况:当2号线程准备获得队列的锁,去获取队列中的元素时,此时1号线程刚好执行完之前的元素操作,返回再去请求队列中的元素,1号线程便获得队列的锁,检查到队列非空,就获取到了3号线程刚刚入队的元素,然后释放队列锁。
6) 等到2号线程获得队列锁,判断发现队列仍为空,1号线程“偷走了”这个元素,所以对于2号线程而言,这次唤醒就是“虚假”的,它需要再次等待队列非空。
解决方法
if 改为 while 判断
如果用if判断,多个等待线程在满足if条件时都会被唤醒(虚假的),但实际上条件并不满足,生产者生产出来的消费品已经被第一个线程消费了。
这就是我们使用while去做判断而不是使用if的原因:因为等待在条件变量上的线程被唤醒有可能不是因为条件满足而是由于虚假唤醒。所以,我们需要对条件变量的状态进行不断检查直到其满足条件,
Synchronized版生产者消费者
1. synchronized 方法或代码块
2. wait();等待
3. notifyAll(); 唤醒全部
package com.it;
/**
* @author shkstart
* @create 2020-06-17 10:11
*/
public class ProCus {
public static void main(String[] args) {
Clerk clerk = new Clerk();
new Thread(()->{
try {
for(int i = 0 ; i <= 10 ;i++){
clerk.dev();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
clerk.sale();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"B").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
clerk.dev();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"C").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
clerk.sale();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"D").start();
}
}
class Clerk{
private int product = 0;
// 生产
public synchronized void dev () throws InterruptedException {
while(product != 0){
wait();
}
product++;
System.out.println(Thread.currentThread().getName() + ">>>" + product);
notifyAll();
}
// 销售
public synchronized void sale() throws InterruptedException {
while(product == 0){
wait();
}
product--;
System.out.println(Thread.currentThread().getName() + ">>>" +product);
notifyAll();
}
}
Lock版生产者消费者
1. Lock lock = new RenntrantLock(); //创建
2. Condition condition = lock.newCondition();
3. lock.lock(); //加锁
4. condition.await(); //等待
5. condition.signall(); //唤醒
6. lock.unlock(); //解锁
package com.it;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author shkstart
* @create 2020-06-17 17:20
*/
public class PC {
public static void main(String[] args) {
Clerk2 clerk2 = new Clerk2();
new Thread(()->{for(int i = 0; i <= 10; i++) {
try {
clerk2.dev();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{for(int i = 0; i <= 10; i++) {
try {
clerk2.sale();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{for(int i = 0; i <= 10; i++) {
try {
clerk2.dev();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{for(int i = 0; i <= 10; i++) {
try {
clerk2.sale();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Clerk2{
private int product = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
// 生产
public void dev () throws InterruptedException {
lock.lock(); // 加锁
try{
while(product != 0){
condition.await(); //等待
}
product++;
System.out.println(Thread.currentThread().getName() + ">>>" + product);
condition.signalAll(); // 唤醒
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
// 销售
public void sale() throws InterruptedException {
lock.lock();
try{
while(product == 0){
condition.await();
}
product--;
System.out.println(Thread.currentThread().getName() + ">>>" +product);
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
}
Condition实现精准唤醒
package com.it;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author shkstart
* @create 2020-06-17 18:00
*/
// 按顺序执行
// A --> B --> C
public class PC2 {
public static void main(String[] args) {
Print print = new Print();
new Thread(()->{
for (int i = 0; i < 10; i++) {
print.printA();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
print.printB();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
print.printC();
}
},"C").start();
}
}
class Print{
private int number = 1;
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
// 1A 2B 3C
public void printA(){
lock.lock();
// 业务,判断-> 执行-> 通知
try{
while (number != 1){
condition1.await();
}
number = 2;
System.out.println(Thread.currentThread().getName() + "=>AAAAAAAAAAA");
condition2.signal(); // 唤醒指定的B
}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() + "=>BBBBBBBBB");
number = 3;
condition3.signal(); // 唤醒指定的C
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try{
while(number != 3){
condition3.await();
}
number = 1;
System.out.println(Thread.currentThread().getName() + "=>CCCCCCCCC");
condition1.signal(); // 唤醒指定的A
}catch (Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
}