文章目录
线程和进程
- java默认2个线程:Main和GC
- 对于java而言 Thread、Runnable、Callable
- java开启不了线程:start是调用native方法创建线程的
线程状态
- NEW :新生
- RUNNABLE :运行
- BLOCKED:阻塞
- WAITING:无限等待
- TIMED_WAITING:超时等待
- TERMINATED:终止
wait/sleep
- 来自不同的类
wait => Object
sleep => Thread
- 关于锁的释放
wait会释放
sleep不会
- 使用的范围不同
sleep:可以再任何地方睡
wait:必须在同步代码块中
- 是否需要捕获异常
wait:不需要捕获异常
sleep:需要,会超时等待
Lock锁
1. 传统Synchronized
public class Demo {
public static void main(String[] args) {
//并发:多线程操作同一个资源类,把资源类丢入线程
Ticket ticket = new Ticket();
//lambda表达式(参数)->{代码}
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"C").start();
}
}
//资源类OOP
class Ticket{
//属性、方法
private int number = 50;
//卖票的方式
//synchronized 本质:锁,排队
public synchronized void sale(){
if(number>0){
System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"剩余:"+number);
}
}
}
2. lock接口
3. ReentrantLock
- 默认非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
//公平锁:先来后到
//非公平锁:可以插队
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
- 例子
/**
* 1. 创建lock
* 2. lock.lock
* 3. lock.unlock
*/
class TicketLock{
//属性、方法
private int number = 50;
//卖票的方式
private 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();
}
}
}
- Lock和Synchronized区别
- Synchronized是内置的java关键字,Lock是一个Java接口
- Synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁
- Synchronized会自动释放锁,lock必须手动释放锁
- Synchronized 线程1(获得锁,阻塞) 线程2(等待…)
lock锁不一定会一直等下去- Synchronized是可重入锁,不可以中断的,非公平
lock可重入锁,可以判断锁,非公平(可以自己设置)- Synchronized适合锁少量的代码同步问题
Lock适合锁大量的同步带吗
传统的生产者、消费者问题
synchronized版
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try{
data.increment();
}catch(Exception e){
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try{
data.decrement();
}catch(Exception e){
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try{
data.increment();
}catch(Exception e){
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try{
data.decrement();
}catch(Exception e){
e.printStackTrace();
}
}
},"D").start();
}
// 等待,业务,通知
class Data{
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();
}
}
- 两个线程没问题,若加上C,D两个线程则会出错
- 虚假唤醒问题
虚假唤醒问题
- 概念
线程也可以唤醒,而不被通知,中断或超时,即虚假唤醒
换句话说,等待应该总是出现在循环中
- 原因
通过if只判断一次,在第一次判断不为0之后进入等待
而在被全部唤醒之后,同时+1
- 解决方法
将wait 的 if 全部改为 while
JUC版生产者消费者问题
- 替代synchronized
class DataJUC{
private int number = 0;
Lock lock = new ReentrantLock();
//用condition的api代替sync
Condition condition = lock.newCondition();
// condition.await();//等待
// condition.signalAll();//唤醒全部
public void increment() throws InterruptedException {
lock.lock();
try {
while (number!=0) {
//等待
condition.await();
}
number++;
//唤醒
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void decrement(){
lock.lock();
try {
while (number==0) {
//等待
condition.await();
}
number--;
//唤醒
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
- 精确的锁和解锁
class DataJUCPrecise{
private Lock lock = new ReentrantLock();
// 利用每个condition.await 和 signal 一一对应
//实现三个线程指定顺序执行
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等待
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"==>AAAAAAAAAAAAA");
number = 2;
//唤醒condition2
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()+"==>BBBBBBBBBBBB");
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()+"==>CCCCCCCCCCCCC");
number = 1;
condition1.signal();
}catch(Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}