【JUC多线程学习】线程、并发与并行、Synchronized与Lock、八锁问题

JUC多线程

java默认有几个线程?

2个 一个是main 一个是GC(垃圾回收)。

java自己可以开启线程吗?

Java不可以开启线程 ,只能通过本地方法调用,Java无法直接操作硬件。

并发与并行

并发:同一时间间隔 多个线程交替执行

并行:多个线程同时执行

线程有6个状态:

  • NEW 新生
  • RUNNABLE 运行
  • BLOCKED 阻塞
  • WATING 等待
  • TIMED_WATING 超时等待
  • TEMINATED 终止

wait和sleep的区别

1、来自不同的类

wait来自Object

sleep来自Thread

2、关于锁的释放

wait会释放锁,sleep睡觉了,抱着锁睡觉,不会释放。

3、使用的范围不同

wait必须在同步代码块中

sleep可以在任何地方

4、是否需要捕获异常

wait不需要捕获异常

sleep必须要捕获异常

传统Synchronized

不加锁的情况:

请添加图片描述

结果会乱序

给sale方法加上锁

请添加图片描述

结果正常。

public class saleTicketDemo01 {
    public static void main(String[] args) {
        //并发: 多线程操作同一个资源类,把资源类丢入线程
        final Ticket ticket = new Ticket();

        new Thread(()->{//lambda表达式 (参数)->{}
            for (int i=0;i<50;i++)
                ticket.sale();
        },"A").start();

        new Thread(()->{
            for (int i=0;i<50;i++)
                ticket.sale();
        },"B").start();

        new Thread(()->{
            for (int i=0;i<50;i++)
                ticket.sale();
        },"C").start();

    }
}

class Ticket{
    //票的数量
    private int num = 30;

    //卖票的操作
    public synchronized void sale(){
        if(num>0)
            System.out.println("卖出第"+(30-num+1)+"张票,还剩"+(--num)+"票");
    }
}

Lock锁

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class saleTicketDemo02 {
    public static void main(String[] args) {
        //并发: 多线程操作同一个资源类,把资源类丢入线程
        final Ticket2 ticket = new Ticket2();

        new Thread(()->{//lambda表达式 (参数)->{}
            for (int i=0;i<50;i++)
                ticket.sale();
        },"A").start();

        new Thread(()->{
            for (int i=0;i<50;i++)
                ticket.sale();
        },"B").start();

        new Thread(()->{
            for (int i=0;i<50;i++)
                ticket.sale();
        },"C").start();

    }
}

class Ticket2{
    //票的数量
    private int num = 30;
    private Lock lock = new ReentrantLock();
    //卖票的操作
    public void sale(){
        lock.lock();//上锁
        try{
            if(num>0)
                System.out.println("卖出第"+(30-num+1)+"张票,还剩"+(--num)+"票");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();//解锁
        }

    }
}

Synchronized和Lock的区别

1、Synchronized是java内置的关键字,Lock是一个java的类

2、Synchronized 无法判断获取锁的状态 Lock可以判断是否获取到了锁

3、Synchronized 会自动释放锁, Lock必须要手动释放锁,如果不释放锁,死锁

4、Synchronized 线程1(获得锁)、线程2(等待,傻傻的等待) Lock锁不一定会等待下去

5、Synchronized 可重入锁不可以中断,非公平; Lock 可重入的,可以判断锁,可以自己设置公不公平

6、Synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码。

生产者消费者问题

传统Synchronized实现

public class demo01 {

    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }

}

//信号量 资源类
class Data{
    private int num = 0;

    //生产者
    public synchronized void increment() throws InterruptedException {
        if(num!=0){
            //等待
            this.wait();
        }
        num++;
        System.out.println(num);
        //唤醒
        this.notifyAll();
    }
    //消费者
    public synchronized void decrement() throws InterruptedException {
        if(num==0){
            //等待
            this.wait();
        }
        num--;
        System.out.println(num);
        //唤醒
        this.notifyAll();
    }
}

存在的问题

两个线程没有问题,多个线程就会出现问题

防止虚假唤醒,等待需要放在循环中

将if 改为while判断

JUC版的生产者消费者

通过lock 获得 conditon

condition.await() 等待

condition.signalAll() 唤醒全部

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class demo02 {

    public static void main(String[] args) {
        Data2 data = new Data2();
        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();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        },"C").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.decrement();
            }
        },"D").start();
    }

}

//信号量 资源类
class Data2{
    private int num = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    //生产者
    public  void increment()  {
        lock.lock();
        try{
            while(num!=0){
                //等待
                condition.await();
            }
            num++;
            System.out.println(Thread.currentThread().getName()+"=>"+num);
            //唤醒
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }

    }
    //消费者
    public synchronized void decrement(){
        lock.lock();
        try{
            while(num==0){
                //等待
                condition.await();
            }
            num--;
            System.out.println(Thread.currentThread().getName()+"=>"+num);
            //唤醒
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

问题:线程乱序的状态

要使它能够有序的执行 A B C D

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class demo03 {
    public static void main(String[] args) {
        Data3 data = new Data3();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.printA();
            }
        },"A").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.printB();
            }
        },"B").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.printC();
            }
        },"C").start();
    }
}

class Data3{
    private int num = 1;
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();

    public void printA(){
        lock.lock();
        try {
            while(num!=1){
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>AAAA");
            num=2;
            condition2.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void printB(){
        lock.lock();
        try {
            while(num!=2){
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>BBBB");
            num=3;
            condition3.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void printC(){
        lock.lock();
        try {
            while(num!=3){
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>CCCC");
            num=1;
            condition1.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

八锁问题

  • 锁的对象是方法的调用者
  • 不是同步方法不受锁定的影响
  • static静态方法 类一加载就有了 锁的是Class类模板唯一的
import java.util.concurrent.TimeUnit;

//1,标准情况下,两个线程先打印  发短信 还是  打电话?   发短信
//2,标准情况下,发短信方法sleep 4s 两个线程先打印  发短信 还是  打电话?   发短信
public class test01 {
    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();

        new Thread(()->{
            phone.sendSms();
        }).start();

        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
            phone.call();
        }).start();

    }
}

class Phone{

    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call(){
        System.out.println("打电话");
    }
}

因为锁的对象 是 方法的调用者, 两个方法用的是同一个锁。谁先拿到就谁先执行

import java.util.concurrent.TimeUnit;

//3,增加一个普通方法,两个线程先打印  发短信 还是  hello?   hello
public class test02 {
    public static void main(String[] args) throws InterruptedException {
        Phone2 phone = new Phone2();

        new Thread(()->{
            phone.sendSms();
        }).start();

        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
            phone.hello();
        }).start();

    }
}

class Phone2{

    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call(){
        System.out.println("打电话");
    }

    public void hello(){
        System.out.println("hello");
    }
}

因为普通方法没有锁,不是同步方法,不受锁的影响

import java.util.concurrent.TimeUnit;

//4,两个对象 都是同步方法  先打印  发短信 还是  打电话?   打电话
public class test02 {
    public static void main(String[] args) throws InterruptedException {
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();

        new Thread(()->{
            phone1.sendSms();
        }).start();

        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
            phone2.call();
        }).start();

    }
}

class Phone2{

    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call(){
        System.out.println("打电话");
    }
    
}

因为现在是两个对象,发短信锁的是phone1对象,打电话锁的是phone2对象,是两把锁

import java.util.concurrent.TimeUnit;

//5,两个静态同步方法  先打印  发短信 还是  打电话?   发短信
public class test03 {
    public static void main(String[] args) throws InterruptedException {
        Phone3 phone = new Phone3();

        new Thread(()->{
            phone.sendSms();
        }).start();

        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
            phone.call();
        }).start();

    }
}
//只有唯一的一个Class对象
class Phone3{

    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public static synchronized void call(){
        System.out.println("打电话");
    }


因为static 静态同步方法 锁的是Class类模板,所以发短信方法先拿了,打电话只能等待

import java.util.concurrent.TimeUnit;

//6,两个静态同步方法,两个对象  先打印  发短信 还是  打电话?   发短信
public class test03 {
    public static void main(String[] args) throws InterruptedException {
        //两个对象只有一个Class
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();
        new Thread(()->{
            phone1.sendSms();
        }).start();

        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
            phone2.call();
        }).start();

    }
}

class Phone3{

    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public static synchronized void call(){
        System.out.println("打电话");
    }

}

static同步方法锁的是Class ,只有一把锁 所以phone1先拿了,先打印发短信

import java.util.concurrent.TimeUnit;

//7,一个普通同步方法,一个静态同步方法,  先打印  发短信 还是  打电话?   打电话
public class test04 {
    public static void main(String[] args) throws InterruptedException {
        Phone4 phone = new Phone4();

        new Thread(()->{
            phone.sendSms();
        }).start();

        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
            phone.call();
        }).start();

    }
}

class Phone4{

    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call(){
        System.out.println("打电话");
    }

}

因为这里是两把锁,静态同步方法锁的是Class 普通同步方法锁的是调用者

import java.util.concurrent.TimeUnit;

//8,一个普通同步方法,一个静态同步方法,两个对象  先打印  发短信 还是  打电话?   打电话
public class test04 {
    public static void main(String[] args) throws InterruptedException {
        Phone4 phone1 = new Phone4();
        Phone4 phone2 = new Phone4();

        new Thread(()->{
            phone1.sendSms();
        }).start();

        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
            phone2.call();
        }).start();

    }
}

class Phone4{

    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call(){
        System.out.println("打电话");
    }

}

与7同理,两个用的不是同一个锁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值