1、什么是JUC?
JUC就是java.util.concurrent。
2、进程和线程
进程:一个应用程序,双击qq就是qq进程启动。
线程:一个进程中包含多个线程,qq中有多个任务,你同事可以和多个人聊天,这每个任务就是一个线程。
Java默认有一个线程?
两个,main线程和GC线程。
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);
//启动线程 start0();
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 */
}
}
}
//本地方法调用底层的c++ java无法直接操作硬件
private native void start0();
3、并发和并行
并发:多个线程“同时”操作一个资源,单核CPU下,CPU在多个任务之间高速的切换,同时只是一种假象。
并行:多个线程可以同时执行。
//查看CPU的核数
System.out.println(Runtime.getRuntime().availableProcessors());
并发编程的本质:充分利用CPU的资源,提高CPU的利用率。
4、线程有几种状态?
6个,源码可以看到:
/*@since 1.5
* @see #getState
*/
public enum State {
//新生
NEW,
//运行
RUNNABLE,
//阻塞
BLOCKED,
//等待,死等,一直等
WAITING,
//超时等待,过时不候
TIMED_WAITING,
//终止
TERMINATED;
}
5.sleep和wait的区别?
(1)来自不同的类
wait()来自于Object类。
sleep()来自于Thread类。
(2)锁的释放
wait()会释放锁,sleep()不会释放锁
(3)使用范围
wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用。
(4)作用
sleep()的作用是让线程休眠指定的时间,在时间到达时恢复线程执行。
wait()是Object的方法,可以说对任意一个对象调用wait()方法,wait()方法会将调用者的线程挂起,知道其他线程调用同一个对象的notify()方法才会重新激活调用者。
6.Lock锁
传统的synchronized实现买票问题
package com.JUC编程.代码;
public class MyDemo4 {
public static void main(String[] args) {
//并发编程:多线程操作同一个资源类
/*public Thread(Runnable target, String name) {}*/
/*new Thread(new Runnable() {
@Override
public void run() {
//操作
}
}).start();*/
Resources resources = new Resources();
new Thread(()->{
for (int i = 0; i < 40; i++) {
resources.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
resources.sale();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
resources.sale();
}
},"C").start();
}
}
//资源类
class Resources{
private static int sales=30;
public synchronized void sale(){
if(sales>0){
System.out.println(Thread.currentThread().getName()+"卖出第"+(sales--)+"张票"+",剩余"+sales+"张票");
}
}
}
Lock锁使用
公平锁**:十分公平
非公平锁:可以插队,Lock默认的就是非公平锁,synchronized也是非公平锁。
public ReentrantLock() {
sync = new NonfairSync();//默认非公平锁
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
//可以设置为公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
package com.JUC编程.代码;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyDemo5 {
public static void main(String[] args) {
Resource resource = new Resource();
new Thread(()->{
for (int i = 0; i < 30; i++) {
resource.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 30; i++) {
resource.sale();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 30; i++) {
resource.sale();
}
},"C").start();
}
}
class Resource{
private int sales=30;
Lock lock=new ReentrantLock();
public void sale(){
lock.lock();
try {
if(sales>0){
System.out.println(Thread.currentThread().getName()+"卖出第"+(sales--)+"张票"+",剩余"+sales+"张票");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
7、synchronized和Lock的区别(重要)
- synchronized是Java内置的关键字,Lock是一个类。
- synchronized会自动释放锁,Lock必须手动释放,如果不释放就会发生死锁现象。
- synchronized无法获取锁的状态,Lock可以判断是否获取到了锁。
- synchronized锁中当一个线程阻塞,另一个线程会一直等待下去,Lock锁不一定会等待下去,tryLock()这个方法可以尝试获取锁。
- synchronized 可重入锁,不可中断,非公平锁,Lock锁 可重入锁,可以判断锁,非公平(可以设置)。
- synchronized 适合少量的代码同步问题,Lock适合大量的同步代码。
8、生产者消费者问题
(1)传统synchronized实现
package com.JUC编程.代码;
public class MyDemo6 {
public static void main(String[] args) {
//生产者消费者 线程间通信
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.PlusOne();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.LessOne();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
//资源类
class Data{
private int number=0;
public synchronized void PlusOne() throws InterruptedException {
if(number!=0){
//等待另一个线程进行-1操作
this.wait();
}
//通知另一个线程进行-1操作
number++;
System.out.println(Thread.currentThread().getName()+"--"+number);
this.notifyAll();
}
public synchronized void LessOne() throws InterruptedException {
if(number==0){
//等待\
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"--"+number);
this.notifyAll();
}
}
我们发现一个问题,上面的代码中,两个线程是对的,但是三个、四个的时候就会出现问题,这就涉及到一个虚假唤醒的问题。
虚假唤醒
当一个条件满足时,很多线程都被唤醒了,但是只有其中部分是有用的唤醒,其它的唤醒都是无用功
解决办法
- 因为if只会执行一次,执行完会接着向下执行if()外边的。
- 而while不会,直到条件满足才会向下执行while()外边的。
package com.JUC编程.代码;
public class MyDemo6 {
public static void main(String[] args) {
//生产者消费者 线程间通信
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.PlusOne();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.LessOne();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.PlusOne();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.LessOne();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
//资源类
class Data{
private int number=0;
public synchronized void PlusOne() throws InterruptedException {
while(number!=0){
//等待另一个线程进行-1操作
this.wait();
}
//通知另一个线程进行-1操作
number++;
System.out.println(Thread.currentThread().getName()+"--"+number);
this.notifyAll();
}
public synchronized void LessOne() throws InterruptedException {
while(number==0){
//等待\
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"--"+number);
this.notifyAll();
}
}
(2)Lock锁实现生产者消费者问题
package com.JUC编程.代码;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyDemo7 {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.PlusOne();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.LessOne();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
//资源类
class Data1{
private int number=0;
Lock lock=new ReentrantLock();
Condition condition = lock.newCondition();
public void PlusOne() throws InterruptedException {
lock.lock();
try {
while(number!=0) {
//等待其他线程进行+1操作
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"--"+number);
//通知其他线程进行-1操作
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void LessOne() throws InterruptedException {
lock.lock();
try {
while(number==0){
//等待其他线程进行—1操作
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"--"+number);
condition.signalAll();
//通知其他线程进行+1操作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
9.Condition实现精准通知唤醒
package com.JUC编程.代码;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyDemo7 {
public static void main(String[] args) {
Data1 data = new Data1();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.PrintA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.PrintB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.PrintC();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.PrintD();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
//资源类
class Data1{
private int number=1;
Lock lock=new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
Condition condition4 = lock.newCondition();
public void PrintA() throws InterruptedException {
lock.lock();
try {
while(number!=1) {
//等待其他线程进行操作
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"--"+number);
number=2;
//通知其他线程进行操作
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void PrintB() throws InterruptedException {
lock.lock();
try {
while(number!=2){
//等待其他线程进行操作
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"--"+number);
number=3;
condition3.signal();
//通知其他线程进行+1操作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void PrintC() throws InterruptedException {
lock.lock();
try {
while(number!=3){
//等待其他线程进行操作
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"--"+number);
number=4;
condition4.signal();
//通知其他线程进行+1操作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void PrintD() throws InterruptedException{
lock.lock();
try {
while(number!=4){
//等待其他线程进行操作
condition4.await();
}
System.out.println(Thread.currentThread().getName()+"--"+number);
number=1;
condition1.signal();
//通知其他线程进行+1操作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
输出结果
A--1
B--2
C--3
D--4
A--1
B--2
C--3
D--4
A--1
B--2
C--3
D--4
......