目录
1、什么是JUC
如上图,就是java.util.concurrent包名的简写,是关于并发编程的API
2、进程和线程
- 进程是程序的一次执行过程,是一个动态概念,是程序在执行过程中分配和管理资源的基本单位,每一个进程都有一个自己的地址空间。
- 线程是CPU调度和分派的基本单位,它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
- 线程是进程的一部分一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
2.1、java默认有几个线程?
JAVA默认有两个线程,main线程和GC线程
2.2、java程序可以自己开启线程吗?
进入到new Thread().start()方法的源码可以看到,start()方法中调用了private native void start0()这么一个方法,用native修饰的,属于c语言写的本地方法,java是运行在虚拟机上的,不能调用硬件。
public synchronized void start() { /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ if (threadStatus != 0) throw new IllegalThreadStateException(); /* Notify the group that this thread is about to be started * so that it can be added to the group's list of threads * and the group's unstarted count can be decremented. */ group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } } private native void start0();
2.3、线程有几个状态
进入Thread.State的源码可以看到,线程默认有6个状态
public enum State { //新生状态 NEW, //运行状态 RUNNABLE, //阻塞状态 BLOCKED, //等待 WAITING, //超时等待 TIMED_WAITING, //终止 TERMINATED; }
2.4、wait和sleep的区别
来自不同的类,wait-->object,sleep-->Thread
wait会释放锁,而sleep不会
wait只能在同步代码块中使用
2.5、synchronized和lock的对比
synchronized是内置的java关键字,lock是一个java接口
synchronized会自动释放锁,lock必须手动释放锁
lock是可以中断锁,synchronized是非中断锁,必须等待线程执行完成释放锁
package com.daiy.demo1;
//synchronized锁
public class SaleOfTicketsDemo {
public static void main(String[] args) {
SaleOfTickets saleOfTickets=new SaleOfTickets();//资源对象
new Thread(() ->{
for (int i = 0; i < 30; i++) {
saleOfTickets.tickets();
}
},"张三").start();
new Thread(() ->{
for (int i = 0; i < 30; i++) {
saleOfTickets.tickets();
}
},"李四").start();
new Thread(() ->{
for (int i = 0; i < 30; i++) {
saleOfTickets.tickets();
}
},"王五").start();
}
}
class SaleOfTickets{
private int num=1;
private int overplus=30;
public synchronized void tickets(){
if(num<=30){
System.out.println(Thread.currentThread().getName()+"把第【"+num+++"】张票卖出去了,剩下【"+--overplus+"】张票");
}
}
}
package com.daiy.demo1;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//lock锁
public class SaleOfTicketsDemo2 {
public static void main(String[] args) {
SaleOfTickets2 saleOfTickets2 = new SaleOfTickets2();//资源对象
new Thread(() -> {
for (int i = 0; i < 30; i++) {
saleOfTickets2.tickets();
}
}, "张三").start();
new Thread(() -> {
for (int i = 0; i < 30; i++) {
saleOfTickets2.tickets();
}
}, "李四").start();
new Thread(() -> {
for (int i = 0; i < 30; i++) {
saleOfTickets2.tickets();
}
}, "王五").start();
}
}
class SaleOfTickets2 {
private int num = 1;
private int overplus = 30;
public void tickets() {
Lock lock = new ReentrantLock();
lock.lock();
try {
if (num <= 30) {
System.out.println(Thread.currentThread().getName() + "把第【" + num++ + "】张票卖出去了,剩下【" + --overplus + "】张票");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
3、生产者消费者问题
当只有两个线程时,代码如下
package com.daiy.demopc; public class PcTest { public static void main(String[] args) { Data data = new Data(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { data.add(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "a线程").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { data.delete(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "b线程").start(); } } class Data { private int num = 0; //+1操作 public synchronized void add() throws InterruptedException { //如果num不等于0线程则需要等待 if (num != 0) { this.wait(); } //否则执行+1, num++; //通知其他线程已经+1完毕 this.notifyAll(); System.out.println(Thread.currentThread().getName() + "加1--->" + num); } //-1操作 public synchronized void delete() throws InterruptedException { //如果num等于0线程则需要等待 if (num == 0) { this.wait(); } //否则执行-1, num--; //通知其他线程已经-1完毕 this.notifyAll(); System.out.println(Thread.currentThread().getName() + "减1--->" + num); } }
执行结果如上,正常,
当线程为2个以上时,在上代码中在加上c线程跟d线程,执行结果如下
执行结果中存在2或者3或者4等,这种情况叫虚假唤醒
当一个条件满足时,很多线程都被唤醒了,但是只有其中部分是有用的唤醒,其它的唤醒都是无用功 比如说买货,如果商品本来没有货物,突然进了一件商品,这是所有的线程都被唤醒了 ,但是只能一个人买,所以其他人都是假唤醒,获取不到对象的锁
3.1为什么会出现虚假唤醒
原因:由于在代码中的data()方法,使用了if判断,由于if只会执行一次,执行完会接着向下执行if()外边的代码,
解决方案:while不会,直到条件满足才会向下执行while()外边的代码。
注意:在if块中使用wait方法,是非常危险的,因为一旦线程被唤醒,并得到锁,就不会再判断if条件,而执行if语句块外的代码,所以建议,凡是先要做条件判断,再wait的地方,都使用while循环来做。
3.2 JUC版生产者消费者代码
Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); condition.await();//等待 condition.signalAll();//唤醒全部
package com.daiy.demopc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class PcTest2 {
public static void main(String[] args) {
Data2 data2 = new Data2();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data2.add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "a线程").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data2.delete();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "b线程").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data2.add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "c线程").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data2.delete();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "d线程").start();
}
}
class Data2 {
private int num = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//condition.await();//等待
//condition.signalAll();//唤醒全部
//+1操作
public void add() throws InterruptedException {
lock.lock();
try {
//如果num不等于0线程则需要等待
while (num != 0) {
condition.await();
}
//否则执行+1,
num++;
//通知其他线程已经+1完毕
condition.signalAll();
System.out.println(Thread.currentThread().getName() + "加1--->" + num);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//-1操作
public void delete() throws InterruptedException {
lock.lock();
try {
//如果num等于0线程则需要等待
while (num == 0) {
condition.await();
}
//否则执行-1,
num--;
//通知其他线程已经-1完毕
condition.signalAll();
System.out.println(Thread.currentThread().getName() + "减1--->" + num);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
3.3condition实现精准通知唤醒
package com.daiy.demopc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* condition实现精准通知唤醒,实现A、B、C依次打印
*/
public class PcTest3 {
public static void main(String[] args) {
Data3 data3 = new Data3();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data3.printA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "a线程").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data3.printB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "b线程").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data3.printC();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "c线程").start();
}
}
class Data3 {
private int num = 1;
Lock lock = new ReentrantLock();
Condition condition_a = lock.newCondition();
Condition condition_b = lock.newCondition();
Condition condition_c = lock.newCondition();
//打印A
public void printA() throws InterruptedException {
lock.lock();
try {
//如果num不等于1,condition_a线程则需要等待
while (num != 1) {
condition_a.await();
}
//num=1则打印A,并让num=2,指定通知condition_b
num = 2;
//指定通知condition_b
condition_b.signal();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "--->AAA");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//打印B
public void printB() throws InterruptedException {
lock.lock();
try {
//如果num不等于2,condition_b线程则需要等待
while (num != 2) {
condition_b.await();
}
//num=2则打印B,并让num=3,指定通知condition_c
num = 3;
//指定通知condition_c
condition_c.signal();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "--->BBB");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//打印C
public void printC() throws InterruptedException {
lock.lock();
try {
//如果num等于0线程则需要等待
while (num != 3) {
condition_c.await();
}
//num=3则打印C,并让num=1,指定通知condition_a
num = 1;
//指定通知condition_a
condition_a.signal();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "--->CCC");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}