搞一个空的Maven项目
确保这里lambda这里是8版本.
JUC全称是Java util Concurrent . java里面的三个包。
在jdk1.8帮助文档里面可以看见 JUC包下面还扩展了一个atomic原子性和一个locks锁。和一个java.util.function包。
java.util 工具包
接触的最早的concurrent就是Callable接口,这玩意就是JUC包下的
JUC——多线程补充_北岭山脚鼠鼠的博客-CSDN博客
java实际不能真正开启线程。在new Thread的底层实际上是调用的native本地方法来开启线程.
并发编程 : 并发、并行
并发(多线程操作同一个资源 )
并行(多个人一起行走 )
//获取CPU核数
//CPU密集型,IO密集型
System.out.println(Runtime.getRuntime().availableProcessors());
并发编程本质:充分的利用CPU资源
/**
* 真正的多线程开发,公司中的开发,降低耦合性。
* 线程就是一个单独的资源类,没有任何附属的作用!
* 1、属性、方法
*/
public class Test1 {
public static void main(String[] args) {
//并发:多线程操作同一个资源类,把资源类丢入线程
Ticket ticket=new Ticket();
//@FunctionalInterface 函数式接口,jdk1.8 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();
}
}
//Lock锁三步走
//1. new ReentrantLock();
//2. lock.lock(); //加锁
//3. lock.unlock(); //解锁
class Ticket{
//属性,方法
private int number=50;
Lock lock=new ReentrantLock();
//卖票的方式
//synchronized 本质: 队列,锁
public void sale(){
lock.lock(); //加锁
try {
//业务代码
if(number>0){
System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock(); //解锁
}
}
}
面试的: 单例模式、排序算法、生产者和消费者、死锁
/**
* 线程之间的通信问题:生产者和消费者问题
* 线程交替执行 A B操作同一个变量 num=0;
* A num+1
* B num-1
*/
public class A {
public static void main(String[] args) {
Data data=new Data();
new Thread(()->{
for(int i=0;i<10;i++) {
try {
data.increment();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"A").start();
new Thread(()->{
for(int i=0;i<10;i++) {
try {
data.decrement();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"B").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.notify();
}
public synchronized void decrement() throws InterruptedException {
if(number==0){
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程
this.notify();
}
}
问题存在,A,B,C,D,E多个线程时是否安全。
会出现如下状况,因为唤醒是一次唤醒其它所有线程,可能会有
虚假唤醒问题
把上面资源类中的if判断改为while判断即可 ,这个也叫做自旋.
wait释放锁后,当别人唤醒你,if你就不需要判断,直接往下执行,while被唤醒后还要重新判断
原本使用的是wait和notify,现在用的Condition对象 ,而不是lock和unlock方法。
在这里会有个await方法找Condition接口下。
然后用监视对象来等待await,通知signal。
代码实现:
使用lock和unlock代替Synchronized,使用await和signal代替wait和notify
public class B {
public static void main(String[] args) {
Data2 data=new Data2();
new Thread(()->{
for(int i=0;i<10;i++) {
try {
data.increment();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"A").start();
new Thread(()->{
for(int i=0;i<10;i++) {
try {
data.decrement();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"B").start();
new Thread(()->{
for(int i=0;i<10;i++) {
try {
data.increment();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"C").start();
new Thread(()->{
for(int i=0;i<10;i++) {
try {
data.decrement();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"D").start();
}
}
//等待,业务,通知
class Data2{
private int number=0;
Lock lock=new ReentrantLock();
Condition condition = lock.newCondition();
// condition.await(); 等待
// condition.signalAll(); 唤醒全部
public void increment() throws InterruptedException {
lock.lock();
try {
while(number!=0){
//等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程
condition.signalAll();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
while(number==0){
//等待
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程
condition.signalAll();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
精准的通知和唤醒,在Synchronized中不能做到这一点。
定义多个同步监视器Condition,我原本以为是直接去唤醒指定线程,没想到是通过唤醒监视器间接唤醒了线程。
/**
* A执行完调用B,B执行完调用C
*/
public class C {
public static void main(String[] args) {
Data3 data3=new Data3();
new Thread(()->{
for(int i=0;i<10;i++)
data3.printA();
},"A").start();
new Thread(()->{
for(int i=0;i<10;i++)
data3.printB();
},"B").start();
new Thread(()->{
for(int i=0;i<10;i++)
data3.printC();
},"C").start();
}
}
/**
* 资源类
*/
class Data3 {
private Lock lock=new ReentrantLock();
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.await();
}
System.out.println(Thread.currentThread().getName()+"=》BBBBB");
//唤醒指定的人B
number=2;
condition2.signal();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
};
public void printB(){
lock.lock();
try {
//业务,判断-》执行-》通知
while(number!=2){
//等待
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"=》CCCCC");
//唤醒指定的人B
number=3;
condition3.signal();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
};
public void printC(){
lock.lock();
try {
//业务,判断-》执行-》通知
while(number!=3){
//等待
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"=》AAAAA");
//唤醒指定的人B
number=1;
condition1.signal();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
};
}
这里肯定是先发短信
/**
* 8锁,关于锁的八个问题
* 1.标准情况下,两个线程先打印 发短信还是 打电话? 1/发短信 2/打电话
*/
public class Test1 {
public static void main(String[] args) {
phone phone=new phone();
new Thread(()->{phone.sendSms();},"A").start();
//捕获异常
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{phone.call();},"B").start();
}
}
class phone{
public synchronized void sendSms(){System.out.println("发短信");}
public synchronized void call(){System.out.println("打电话");}
}
还是发短信先。
在第一个代码里面可能会觉得是因为主线程sleep了1s所以导致A先执行,但是这种想法是错误的。
下面这个代码里面A线程里面睡了4s也照样是先发短信。
synchronized 锁住的对象是方法的调用者! 两个方法用的是同一把锁,所以谁先拿到谁执行
phone类只有一个,A先B一步拿到了phone的锁,并且sleep时没有放开
/**
* 8锁,关于锁的八个问题
* 1.标准情况下,两个线程先打印 发短信还是 打电话? 1/发短信 2/打电话
* 2.sendSms延迟4s,标准情况下,两个线程先打印 发短信还是 打电话? 1/发短信 2/打电话
*/
public class Test1 {
public static void main(String[] args) {
phone phone=new phone();
new Thread(()->{phone.sendSms();},"A").start();
//捕获异常
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{phone.call();},"B").start();
}
}
class phone{
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("发短信");
}
public synchronized void call(){System.out.println("打电话");}
}
因为hello是普通方法,不是同步方法,不受锁的影响,所以是先输出hello再输出发短信。
/**
* 3.增加了一个普通方法hello,先发短信还是hello
*/
public class Test2 {
public static void main(String[] args) {
phone2 phone=new phone2();
new Thread(()->{phone.sendSms();},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{phone.hello();},"B").start();
}
}
class phone2{
//synchronized 锁住的对象是方法的调用者!
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("发短信");
}
public synchronized void call(){System.out.println("打电话");}
public void hello(){
System.out.println("hello");
}
}
这里创建了两个phone对象执行两个同步方法,最后是先输出打电话。
原因:
两个不同的phone对象,有两把锁
/**
* 3.增加了一个普通方法hello,先发短信还是hello
* 4.两个对象,两个同步方法,发短信还是打电话?
*/
public class Test2 {
public static void main(String[] args) {
//两个对象
phone2 phone1=new phone2();
phone2 phone2=new phone2();
new Thread(()->{phone1.sendSms();},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{phone2.call();},"B").start();
}
}
class phone2{
//synchronized 锁住的对象是方法的调用者!
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("发短信");
}
public synchronized void call(){System.out.println("打电话");}
public void hello(){
System.out.println("hello");
}
}
静态方法是类的方法,不管有几个对象,静态方法都只有一个。并且类一加载就有了。
上面4个代码锁的都是new出来的phone对象,但是这里锁的是Class。
/**
* 5.增加两个静态的同步方法,只有一个对象,先哪个?
*/
public class Test3 {
public static void main(String[] args) {
//两个对象
phone3 phone=new phone3();
new Thread(()->{phone.sendSms();},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{phone.call();},"B").start();
}
}
class phone3{
//synchronized 锁住的对象是方法的调用者!
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("发短信");
}
public static synchronized void call(){System.out.println("打电话");}
}
还是先发短信。因为锁住的还是全局唯一的Class类.
/**
* 5.增加两个静态的同步方法,只有一个对象,先哪个?
* 6.两个对象调用两个静态同步方法,先哪个?
*/
public class Test3 {
public static void main(String[] args) {
//两个对象
phone3 phone1=new phone3();
phone3 phone2=new phone3();
new Thread(()->{phone1.sendSms();},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{phone2.call();},"B").start();
}
}
class phone3{
//synchronized 锁住的对象是方法的调用者!
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("发短信");
}
public static synchronized void call(){System.out.println("打电话");}
}
这里是先打电话。
原因:
静态那个锁的是phone的class模板,普通的那个锁的是phone对象。有两个锁,所以B不需要等待A释放锁。
/**
* 7.一个普通同步,一个静态同步,一个对象,先哪个?
*/
public class Test4 {
public static void main(String[] args) {
//两个对象
phone4 phone=new phone4();
new Thread(()->{phone.sendSms();},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{phone.call();},"B").start();
}
}
class phone4{
//静态同步方法
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("发短信");
}
//普通同步方法
public synchronized void call(){System.out.println("打电话");}
}
这个也是先输出打电话。
还是因为两个锁锁的东西不一样。
/**
* 7.一个普通同步,一个静态同步,一个对象,先哪个?
* 8.一个普通同步,一个静态同步,两个对象,先哪个?
*/
public class Test4 {
public static void main(String[] args) {
//两个对象
phone4 phone1=new phone4();
phone4 phone2=new phone4();
new Thread(()->{phone1.sendSms();},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{phone2.call();},"B").start();
}
}
class phone4{
//静态同步方法
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("发短信");
}
//普通同步方法
public synchronized void call(){System.out.println("打电话");}
}
这里只要确定锁的两个东西,一个是this对象,一个是Class模板。