javase:线程同步

互斥锁

互斥锁介绍

①互斥锁是一个互斥的同步对象,意味着同一时间间有且仅有一个线程可以获取它
②互斥锁可适用于一个共享资源,每次只能被一个线程访问的情况

互斥锁特性

互斥锁存在于每个对象中,通过利用互斥锁(特殊的标记)实现某一个时间上只能有一个线程对这个对象进行操作

临界资源

多个线程共享同一个资源,那么这个资源就叫临界资源

synchronize块

synchronize关键字

①通过synchronize关键字,间接的使用互斥锁
②使用synchronize关键字修饰的方法。表示执行该方法是,当前对象只能被一个线程使用。这个线程使用完,才能被其他线程使用

synchronize块

①synchronize可以修饰一片代码段
语法:synchronize(object){//代码} Object就是对象锁,object中可以是产生的其他对象,也可以是this,this表示临界资源
②synchronize可以修饰方法
等价于:synchronize(this){//代码块}
③不同点:synchronize修饰方法的作用范围比synchronize(this){}范围大

生产者/消费者

生产者消费者模型

①provider生产者线程负责生产数据
②consumer 消费者线程负责消费生成的数据
在这里插入图片描述

多线程实现生产者和消费者案例

Stack.java

package ThreadDemo.ProvideAndConsumer;

import java.util.Stack;

public class StackTest {

    public static void main(String[] args) {
        //1.创建一个栈
        Stack<String> stack = new Stack<>();
        //2.创建两个线程
        ProvideDemo p1 = new ProvideDemo(stack);
        ProvideDemo p2 = new ProvideDemo(stack);
        ProvideDemo p3 = new ProvideDemo(stack);

        ConsumerDemo c1 = new ConsumerDemo(stack);
        ConsumerDemo c2 = new ConsumerDemo(stack);
        ConsumerDemo c3 = new ConsumerDemo(stack);

        //3.启动线程
        p1.start();
        p2.start();
        p3.start();

        c1.start();
        c2.start();
        c3.start();

    }

}

ProvideDemo.java

package ThreadDemo.ProvideAndConsumer;

import ThreadDemo.ThreadDemo;

import java.util.Stack;

public class ProvideDemo extends Thread{
    private Stack<String> stack;
    private int start = 1;
    public ProvideDemo(Stack stack){
        this.stack = stack;
    }
    @Override
    public void run(){
        while(true){
            synchronized (stack){
                stack.push(start+"");
                System.out.println("入栈内容为:"+start);
                System.out.println("栈中数据为:"+stack);
                start++;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

ConsumerDemo.java

package ThreadDemo.ProvideAndConsumer;

import java.util.Stack;

public class ConsumerDemo extends Thread{
    private Stack<String> stack;
    public ConsumerDemo(Stack stack){
        this.stack = stack;
    }

    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (stack){
                if(!stack.isEmpty()){
                    String data = stack.pop();
                    System.out.println("出栈内容为:"+data);
                    System.out.println("栈中数据为:"+stack);
                }
            }
        }
    }
}

死锁及解决方案

死锁概念

是指两个或两个以上的进程(线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外部处理作用,它们都将无限等待下去

死锁原因

①系统资源不足②进程(线程)推进的顺序不恰当③资源分配不当

死锁形成的条件

①互斥条件:所谓互斥就是进程在某一时间独占资源
②请求与保持联系:一个进程因请求资源而阻塞时,对已获得的资源保持不放
③不剥夺条件:进程已获得资源,在未使用完之前,不能强行剥夺
④循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系

死锁形成的场景

①忘记释放锁②单线程重复申请锁③多线程多锁申请④多线程多锁生气

避免死锁

①加锁顺序(线程按照一定的顺序加锁)
②加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占用的锁)
③死锁检测(每当一个线程获得了锁,会在线程和锁相关的数据结构中(map,graph等)将其几下。除此之外,每当有线程请求锁,也需要记录在这个数据结构之中)

线程休眠与唤醒

Object类的API

①wait();当前线程要释放锁定资源,但要进入阻塞状态。使用wait()方法要释放对象锁
②notify()是要解除阻塞进入对象池中等待获取对象锁
③notifyAll()解除阻塞池中所有线程。让其进入到阻塞对象池中,去争夺对象锁

案例:利用线程交叉输出奇数和偶数

Number.java

package ThreadDemo.NumberDemo;

public class Number {
    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }


    private int no ;

    public Number(int no){
        super();
        this.no = no;
    }
}

NumberDemo.java

package ThreadDemo.NumberDemo;


public class NumberDemo {

    public static void main(String[] args) {
        //1.定义一个数字(临界资源)
        Number no = new Number(1);
        //2.创建线程资源
        NumberThread nt = new NumberThread(no);
        //3.创建线程
        Thread t1 = new Thread(nt);
        Thread t2 = new Thread(nt);
        //
        t1.start();
        t2.start();

    }

}

class NumberThread implements Runnable{
    private Number no;
    public NumberThread(Number no){
        this.no = no;
    }
    @Override
    public void run() {
        while(true){
            synchronized (no){
                int num = no.getNo();
                if(num % 2 == 0 ){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    even();
                    num++;
                    no.setNo(num);
                    no.notifyAll();
                    try {
                        no.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    continue;
                }
                if(num % 2 == 1){
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    odd();
                    num++;
                    no.setNo(num);
                    no.notifyAll();
                    try {
                        no.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    continue;
                }

            }
        }
    }

    public void even(){
        System.out.println(no.getNo());
    }

    public void odd(){
        System.out.println(no.getNo());

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值