一,什么是JUC
JUC是java.util.concurrent包的缩写,用于多线程并发编程,为了处理高并发任务。
三个工具包,可以看帮助文档,想看的话点链接api帮助文档
先记住一点:普通线程代码用Thread,Runnable没有返回值并且效率比Callable要低(有个印象,后面会证明)
二,线程和进程的关系
(1)举一个例子
你用qq,当年打开qq时,这是开启了一个进程,这时你玩qq,同时和你朋友打视频然后给另一个朋友发信息,还可以看qq空间,其中就涉及了三个线程。从中可以得知一个进程可以包含多个线程,至少有一个线程。
(2)java默认的线程有几个?
两个 main线程和GC(垃圾回收器)线程 第二个需要你了解JVM
(3)面试题:java可以开启线程?
开不了,给你看看代码
第一段代码
public class demo01 {
public static void main(String[] args) {
new Thread().start();
}
}
第二段代码 进入start()里面的方面 (不用看全部代码)
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
第三段代码 进入start0();
private native void start0();
此时发现有native关键字,这是需要你了解jvm体系结构,有一个本地方法栈,调用了本地方法,在底层,就是java代码调用了一个非java代码写的接口,如果你了解操作系统,操作系统给用户提供了程序接口(通过程序调用)和命令接口(直接调用),主要作用就是隐藏复杂的实现细节,你只需调用我就可以实现功能,从中就可得知,线程的开启是操作系统帮我们完成的
(4)并发和并行
假如有两个线程,
若cpu只有一核,此时两个线程同时是start(),此时就会出现并发现象,只有一个运行完了才能下一个,并发是一种快速交替,让你感觉好像是同时进行,但实际上不是。
若cpu是二核,则是真正的并行,同时运行。
并发编程本质:充分利用cpu的资源。并行可以用线程池来解决。
public class demo01 {
public static void main(String[] args) {
//获取cpu的核数
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
三,线程的状态
(1)直接上代码
第一段
public class demo01 {
public static void main(String[] args) {
Thread.State
}
}
第二段直接进入State,发现是一个枚举
public enum State {
//新生,就绪
NEW,
//运行
RUNNABLE,
//阻塞
BLOCKED,
//等待 会一直等下去
WAITING,
//超时等待 不会
TIMED_WAITING,
//死亡
TERMINATED;
}
(2)面试题:请回答wait()和sleep()这两个方法的区别
第一:所属的类不一样 wait方法是Object类 而sleep方法是Thread类
第二:使用的范围不一样,wait方法只能在同步代码块中,而sleep在哪里都可以
第三:wait方法会使调用者释放锁资源,而sleep不会让调用者释放锁资源
第四:wait不需要捕获异常,而sleep需要捕获异常,因为怕发生超时等待
四,lock锁(重点)
1,传统的synchronized
上代码
public class demo01 {
public static void main(String[] args) {
Ticket ticket= new Ticket();
new Thread(
()->{
for (int i = 0; i < 30; i++) {
ticket.sale();
}
},"A").start();
new Thread(()->{for (int i = 0; i < 30; i++) {
ticket.sale();
}},"B").start();
new Thread(() ->{for (int i = 0; i < 30; i++) {
ticket.sale();
}},"C").start();
}
}
class Ticket
{
private int number=50;
public void sale()
{
if(number>0)
{
System.out.println(Thread.currentThread().getName()+"买的票号是"+(number--)+"还剩余"+number+"张票");
}
}
}
会有问题加上synchronized就可以解决
class Ticket
{
private int number=50;
public synchronized void sale()
{
if(number>0)
{
System.out.println(Thread.currentThread().getName()+"买的票号是"+(number--)+"还剩余"+number+"张票");
}
}
}
2,lock锁的实现类
分别对于 可重入锁,读锁,写锁
了解两个概念
1,公平锁:先来后到,十分公平
2,非公平锁:可以插队,不公平(默认)
3,可重入锁:它允许同一个线程多次获取同一个锁。也就是一个线程获取了锁资源,还可以继续获取同一个锁资源,避免死锁现象发生。
为什么默认非公平锁:假想一个场景有两个线程来了第一个要运行三小时,第二个运行三秒,假设用公平锁那么三秒的线程要等三小时,若用非公平锁,则三小时的只用等三秒,主要减少等待时间。
上代码:先示例化,然后进入Reentrantlock()
Lock lock= new ReentrantLock();
public ReentrantLock() {
sync = new NonfairSync();//非公平锁
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();//公平锁
}
这是使用
第一步:首先示例化一个Lock的引用lock
第二步:加锁lock.lock();
第三步:解锁lock.lock();
class Ticket
{
private int number=50;
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();
}
}
}
3,synchronized和lock锁的区别
第一:synchronized是java内置的关键字,lock是java里面的一个接口
第二:synchronized不可以判断获取锁的状态,而lock可以判断是否获取到了锁()
第三:synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手动释放锁(unlock()方法释放锁),否则容易造成线程死锁;
第四:用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
第五:synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)、
第六:Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。
4,生产者和消费者问题
一般面试笔试问题:单例模式,排序算法,生产者消费者,死锁
生产者消费者其实就三步
第一步:判断是否需要等待
第二步:执行业务
第三步:通知
上代码
public class demo02 {
public static void main(String[] args) {
Data data =new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.increment();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.decrement();
}
},"B").start();
}
}
class Data{
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);
notifyAll();
}
public synchronized void decrement (){
if(number==0)
{
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
notifyAll();
}
}