什么是JUC?
先看看如何创建普通的线程代码?通过API了解一下Callable接口和Lock锁
new Thread
实现Runnable接口 (没有返回值),效率相比于Callable相对较低,更多时候使用Callable
Callable接口
Lock接口
说一点常识
线程和进程的区别
进程:
一个程序,比如 IDEA 是一个程序的集合
一个进程可以包含多个线程,至少1个
线程:
我们打开了一个一个笔记本程序(进程) 我们可以写字,可以自动保存,可以自动同步(线程)
java默认有几个线程?
main线程,和GC垃圾回收
java真的可以开启线程吗?
答案:开不了,看源码
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) {
}
}
}
private native void start0();
private native void start0(); 是 本地方法,底层的C++执行,java无法直接操作硬件
并发与并行
并发:
多线程操作同一资源(边吃饭边吃菜)
CPU一核,模拟出来多条线程,快速交替
并行:
多个人一起行走(边吃饭边看电视)
CPU多核,多个线程可以同时执行
public static void main(String[] args)
{
//获取cpu核数
//CPU密集型 IO密集型
System.out.println((Runtime.getRuntime().availableProcessors()));
}
并发编程的本质,充分利用CPU的资源
线程有几个状态 ?
public enum State {
NEW, //新建
RUNNABLE, //运行
BLOCKED, //阻塞
WAITING, //等待 (死死等待)
TIMED_WAITING, //超时等待(有时效性的等待)
TERMINATED; //终止
}
还是卖票的例子
Synchronized 锁
/**
* 基本卖票的列子
* 真正公司中多线程的开发,降低耦合性 static class Ticket 如果 实现 Runnable接口就不是oop开发 耦合性很高
* 线程就是一个单独的资源类,没有任何附属操作 (熟悉,方法)
*
*/
public class Demo01
{
public static void main(String[] args)
{
//并发,多个线程操作同一个资源类,把资源类丢入线程
Ticket ticket =new Ticket();
//lambda new Thread((参数)->{ 代码 }).start(); 三个线程操作同一个资源,并发
new Thread(()->{for(int i =0;i <50;i++) ticket.sale();},"Y1").start();
new Thread(()->{for(int i =0;i <50;i++) ticket.sale();},"Y2").start();
new Thread(()->{for(int i =0;i <50;i++) ticket.sale();},"Y3").start();
}
//资源类 OOP
static class Ticket {
//属性 方法
private int TicketNumber=50;
//卖票的方式 传统的synchronized锁
public synchronize void sale(){
if(TicketNumber>0){
//卖票
System.out.println(Thread.currentThread().getName()+"卖出了第"+TicketNumber+"张票"+",剩余"+--TicketNumber);
}
}
}
}
Lock锁
ReentrantReadWriteLock.ReadLock 读锁
ReentrantReadWriteLock.WriteLock 写锁
ReentrantLock 可重入锁
用Lock锁完成刚才的synchronized卖票的操作
public class Demo
{
public static void main(String[] args)
{
//并发,多个线程操作同一个资源类,把资源类丢入线程
Ticket ticket =new Ticket();
//lambda new Thread((参数)->{ 代码 }).start(); 三个线程操作同一个资源,并发
new Thread(()->{for(int i =0;i <50;i++) ticket.sale();},"Y1").start();
new Thread(()->{for(int i =0;i <50;i++) ticket.sale();},"Y2").start();
new Thread(()->{for(int i =0;i <50;i++) ticket.sale();},"Y3").start();
}
//资源类 OOP
static class Ticket {
//属性 方法
private int TicketNumber=50;
Lock lock = new ReentrantLock();
//卖票的方式
public synchronized void sale(){
//加锁
lock.lock();
try
{
if(TicketNumber>0)
{
//卖票
System.out.println(Thread.currentThread().getName()+"卖出了第"+TicketNumber+"张票"+",剩余"+--TicketNumber);
}
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
//解锁
lock.unlock();
}
}
}
}
Lock锁与Synchronized锁的区别
1,Synchronized 内置的java关键字,Lock是一个java类
2,Synchronized 无法判断获取锁的状态,Lock可以判断是否获取到了锁
3,Synchronized 全自动的,会自动释放锁,Lock要手动释放锁,如果不释放锁会 死锁
4,Synchronized 线程1(获得锁,阻塞了),线程2(等待,傻傻的等待),Lock锁就不一定会等待下去 tryLock()
5,Synchronized 可重入锁,不可以被中断的非公平(无法修改,因为是关键字)
Lock是可重入锁,可以判断锁,可以设置公平锁,非公平锁
6,Synchronized 适合锁少量代码块同步问题,Lock适合锁大量的同步代码
生产者消费者问题
Synchronized版 wait notify
public class A
{
public static void main(String[] args)
{
Data data = new Data();
new Thread(()->{
for(inti =0;i <10;i++)
{
try
{
data.increment();
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for(int i =0;i <10;i++)
{
try
{
data.decrement();
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
},"B").start();
}
//等待 业务 通知
static class Data
{
private int number = 0;
//+1
public synchronized void increment()throwsInterruptedException
{
if (number!=0)
//等待
this.wait();
number++;
System.out.println(Thread.currentThread().getName()+"======"+number);
//通知其他线程 加一完毕
this.notifyAll();
}
//-1
public synchronized void decrement()throwsInterruptedException
{
if (number==0)
//等待
this.wait();
number--;
System.out.println(Thread.currentThread().getName()+"======"+number);
//通知其他线程 减一完毕
this.notifyAll();
}
}
}
上面的代码存在的问题:
如果线程数大于两个:if(number==0)/if(number!=0)只会判断一次,多线程的情况下,if判断会导致虚假唤醒问题,所以要使用while判断
wait后锁被释放了,线程被唤醒只是进入了一个就绪阶段不能马上运行,但在这期间其他线程可能做了操作
wait会释放锁,等他再获取锁的时候,会从释放的地方接着向下执行,所以跳过了if判断
Lock 版生产者和消费者问题
通过Lock可以找到Condition
Condition 精准的通知可唤醒线程
public class C
{
public static void main(String[] args)
{
Data data =new Data();
//A线程执行完去调用B B------->C------>A
new Thread(()->{for(int i =0;i <5;i++) data.printA();},"A").start();
new Thread(()->{for(int i =0;i <5;i++) data.printB();},"B").start();
new Thread(()->{for(int i =0;i <5;i++) data.printC();},"C").start();
}
//资源类
static classData
{
private Lock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
private Condition conditionC = lock.newCondition();
private int number=1;//标志位 1A 2B 3C
public voidprintA()
{
lock.lock();
//业务: 判断 —— 执行——通知
try
{
while(number!=1)
{
//等待
conditionA.await();
}
//执行
System.out.println(Thread.currentThread().getName()+"A");
//唤醒B number赋值
number=2;
conditionB.signal();
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
lock.unlock();
}
}
public void printB()
{
lock.lock();
try
{
while(number!=2)
{
conditionB.await();
}
System.out.println(Thread.currentThread().getName()+"B");
number=3;
conditionC.signal();
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
lock.unlock();
}
}
public void printC()
{
lock.lock();
try
{
while(number!=3)
{
conditionC.await();
}
System.out.println(Thread.currentThread().getName()+"C");
number=1;
conditionA.signal();
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
lock.unlock();
}
}
}
}
八锁现象
那么到底什么是锁?锁的对象是什么呢?
深刻理解锁
下面代码执行是先打电话还是先发短信呢?
public static void main(String[] args)
{
Phone phone = new Phone();
new Thread(()->{phone.sendMsg();},"sendMsg").start();
try
{
TimeUnit.SECONDS.sleep(1);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
new Thread(()->{phone.call();},"call").start();
}
//资源类
static class Phone
{
public synchronized void sendMsg()
{
try
{
TimeUnit.SECONDS.sleep(4);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call()
{
System.out.println("打电话");
}
}
永远都是先发短信 因为synchronized锁的对象是方法的调用者 由于这两个方法都是调用的phone 所以是谁先拿到锁谁先执行
是先发短信还是先hello?
//是先发短信还是先hello?
public static void main(String[] args)
{
Phone phone = new Phone();
new Thread(()->{phone.sendMsg();},"sendMsg").start();
try
{
TimeUnit.SECONDS.sleep(1);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
new Thread(()->{phone.hello();},"hello").start();
}
//资源类
static class Phone
{
public synchronized void sendMsg()
{
try
{
TimeUnit.SECONDS.sleep(4);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call()
{
System.out.println("打电话");
}
public voidhello()
{
System.out.println("hello");
}
}
先hello,因为hello是普通方法 不是同步方法 不受锁的影响
先打电话还是先发短信呢?
public static void main(String[] args)
{
//两个对象
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(()->{phone.sendMsg();},"sendMsg").start();
try
{
TimeUnit.SECONDS.sleep(1);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
new Thread(()->{phone1.call();},"call").start();
}
//资源类
static class Phone
{
public synchronized void sendMsg()
{
try
{
TimeUnit.SECONDS.sleep(4);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call()
{
System.out.println("打电话");
}
}
先打电话,因为synchronized锁的是对象的调用者,因为是两个不同的对象,所以有两把锁,所以按着时间来执行了
先打电话还是短信呢?
public static void main(String[] args)
{
Phone phone = new Phone();
new Thread(()->{phone.sendMsg();},"sendMsg").start();
try
{
TimeUnit.SECONDS.sleep(1);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
new Thread(()->{phone.call();},"call").start();
}
//资源类
static class Phone
{
public static synchronized void sendMsg()
{
try
{
TimeUnit.SECONDS.sleep(4);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call()
{
System.out.println("打电话");
}
}
发短信,因为类两个方法都是static方法 锁锁住的是phone.class,两个方法公用了一把锁
现在创建两个对象,是先发短信还是先打电话呢?
public static void main(String[] args)
{
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(()->{phone.sendMsg();},"sendMsg").start();
try
{
TimeUnit.SECONDS.sleep(1);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
new Thread(()->{phone1.call();},"call").start();
}
//资源类
static class Phone
{
public static synchronized void sendMsg()
{
try
{
TimeUnit.SECONDS.sleep(4);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call()
{
System.out.println("打电话");
}
}
发短信,两个对象的类模版只有一个,所以只要加了static的方法,锁的就是class
先发短信还是打电话呢?
public static void main(String[] args)
{
Phone phone = new Phone();
new Thread(()->{phone.sendMsg();},"sendMsg").start();
try
{
TimeUnit.SECONDS.sleep(1);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
new Thread(()->{phone.call();},"call").start();
}
//资源类
static class Phone
{
//静态的同步方法
public static synchronized void sendMsg()
{
try
{
TimeUnit.SECONDS.sleep(4);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
System.out.println("发短信");
}
//普通的同步方法
public synchronized void call()
{
System.out.println("打电话");
}
}
打电话,因为static方法锁的是class模版,普通同步方法锁的是调用者,两个方法两把锁
和上面一样,两个对象用了两把锁,是先打电话,两个方法两把锁按时间执行
public static void main(String[] args)
{
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(()->{phone.sendMsg();},"sendMsg").start();
try
{
TimeUnit.SECONDS.sleep(1);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
new Thread(()->{phone1.call();},"call").start();
}
//资源类
static class Phone
{
//静态的同步方法 锁的class模版
public static synchronized void sendMsg()
{
try
{
TimeUnit.SECONDS.sleep(4);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
System.out.println("发短信");
}
//普通的同步方法 锁的调用对象
public synchronized void call()
{
System.out.println("打电话");
}
}
总结:
synchronized锁的是对象的调用者
new this 是具体的的一个手机对象
static Class 是Phone.class调用 是唯一的一个模版