线程同步的实现

本文介绍了Java中线程同步的机制,包括synchronized关键字、ReentrantLock锁、死锁问题以及生产者消费者问题的解决方案。同时讨论了信号灯和线程池的概念,强调了在并发编程中正确使用锁的重要性。
摘要由CSDN通过智能技术生成

1.线程同步机制:

多个线程同时访问同一个资源:为了保证数据再方法被访问时的正确性,在访问时加入锁的机制:synchronized。当一个线程获得对象的排他锁,独占资源,其他线程必须等待。

三大不安全问题:

买票:

银行账户取钱:

集合不安全:

Synchronized 方法

同步方法:给方法申明为synchronized ,相当于去拿对象资源的那把锁。保证线程的安全性。默认是锁自己 this。


public synchronized void test(){

    xxxxx
} 

同步代码块:Synchronized 代码块可以锁任何的对象。

Synchronized (xxx) {
    
}

死锁:

双方都持有自己的资源,不进行释放,同时还想要对方的资源。

解决:保证一个资源只能有一个人所持有。

Lock锁:

ReentrantLock (可重入锁) 类实现了Lock锁,与synchronized 相同的语义。

对比synchronized : Lock锁是显示的锁,需要手动开启锁和关闭锁。synchronized 是隐私锁。

package com;

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

/**
 * @author Lenovo
 * @date 2024/3/11
 * @time 21:02
 * @project Chapter01_Java_多线程
 **/
public class TestLock {
    public static void main(String[] args) {
        //实例化一个Runnable的对象:
        Lock lock1 = new Lock();
        new Thread(lock1).start();
        new Thread(lock1).start();
    }
}


class Lock implements Runnable{

    private int ticketNubmer=10;

    //加入ReentrantLock锁:
    ReentrantLock lock= new ReentrantLock();
    @Override
    public void run() {
        while (true){
            if (ticketNubmer>0){

                try {
                    //枷锁
                    lock.lock();
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("当前票为"+ticketNubmer--);
                }finally {
                    //释放锁
                    lock.unlock();
                }

            }else {
                break;
            }
        }
    }
}

生产者和消费者问题:

解决的办法:

管程法:使用缓冲区: (重点)

(消费者等待wait; 通知生产者生产notifyAll)

重点:都需要给方法或者代码块加上锁。

package com.chapter01_多线程;

/**
 * @author Lenovo
 * @date 2024/3/12
 * @time 9:15
 * @project Chapter01_Java_多线程
 **/
public class ProductorAndConsumer {

    public static void main(String[] args) {
        Buffer buffer = new Buffer();

        Productor productor = new Productor(buffer);
        Consumer consumer = new Consumer(buffer);//传入缓存;
        //多线程:
        new Thread(productor).start();
        new Thread(consumer).start();

    }
}

class Productor implements Runnable{
    Buffer buffer;
    //构造器
    public Productor(Buffer buffer) {
        this.buffer = buffer;
    }

    //生产:
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                buffer.push(new Product(i)); //放入产品
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("生产者生产了多少产品" + i);
        }
    }
}

class Consumer implements Runnable{
    //缓冲区属性:
    Buffer buffer;
    //构造器
    public Consumer(Buffer buffer) {
        this.buffer = buffer;
    }
    //消费产品
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消费者消费了"+ buffer.pop().id+"产品");
        }
    }
}
//产品id
class Product {
    public int id;
    public Product(int id) {
        this.id = id;
    }
}

//缓存区
class Buffer{
    //缓冲区大小:
    Product[] productsBuffer=new Product[10];
    //计数器计数:
    int count=0;
    //生产者生产产品
    public synchronized void push(Product product) throws InterruptedException {
        if (count == productsBuffer.length){
            //通知消费者消费;生产者等待:
            this.wait();
        }
        //生产产品:
        productsBuffer[count]= product;//放入产品
        count++;
        //通知消费者消费:
        this.notifyAll();
    }
    //消费者消费产品: 要加上锁:
    public synchronized Product pop(){
        if (count == 0){
            //通知生产者生产;消费者等待;
            try {
                this.wait();
            }catch (Exception e){
            }

        }
        //消费:
        count-- ;  //数组从0开始;
        //拿出产品:
        Product product = productsBuffer[count];
        //吃完了通知生产者生产:
        this.notifyAll();
        return product;
    }
}

信号灯:(重点)通过一个标志位flag 进行标志进区分。

package com.chapter01_多线程;

/**
 * @author Lenovo
 * @date 2024/3/12
 * @time 12:47
 * @project Chapter01_Java_多线程
 **/
public class ProductorAndConsumer02 {

    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();//继承了Thread类
        new Watcher(tv).start(); //继承了Thread类

    }
}

class Player extends Thread{
    TV tv;
    public Player(TV tv) {
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i % 2== 0){
               tv.play("三锅演绎");
            }else {
                tv.play("西游记");
            }
        }
    }
}

class Watcher extends Thread{

    TV tv;
    public Watcher(TV tv) {
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            //观众只管观看:
            tv.watch();
        }
    }
}

class TV{

    String name;
    boolean flag=true; //为真表演者表演,为假是观看者观看:
    public synchronized void play(String name){

        //演员等待:
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("演员表演了:"+name);
        //通知观众观看:
        this.notifyAll();
        this.name=name;
        this.flag=!this.flag; //取反

    }
    //要加上锁:synchronized 
    public synchronized void watch(){
        //观众等待:
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观众观看了:"+name);
        //通知表现者表演:
        this.notifyAll();
        this.flag=!this.flag;
    }
}

线程池:保证资源不会被浪费

实现Runnable 接口可以直接使用 excute。

思考

有什么想法或心得体会,都可以拿出来分享下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值