线程同步问题

news/2024/5/17 19:42:28 标签: java, synchronized, lock, 死锁, 线程池
lock" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);">

文章目录

      • 线程同步
        • 死锁
        • 买票案例
        • Lock的使用
        • 生产者和消费者问题
          • 管程法解决
          • 信号灯法解决
        • 线程池

线程同步

死锁

java">
/**
 * 死锁:
 * 多个线程各自占有一些共享资源﹐并且互相等待其他线程
 * 占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源﹐都停止执行的情形.
 * 某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题.
 *
 * 产生死锁的四个必要条件:
 * 1.互斥条件:一个资源每次只能被一个进程使用。
 * 2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
 * 3. 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。
 * 4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
 */

买票案例

不安全的买票

java">/**
 * 线程同步:
 * 由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题﹐为了保证数据在方法中被访问时的正确性﹐在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,独占资源﹐其他线程必须等待,使用后释放锁即可.存在以下问题:
 *  1. 一个线程持有锁会导致其他所有需要此锁的线程挂起;
 *  2. 在多线程竞争下﹐加锁﹐释放锁会导致比较多的上下文切换和调度延时,引起性能问题;
 *  3. 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置﹐引起性能问题.
 */
public class UnTicket {
    public static void main(String[] args) {
        BuyTicket station = new BuyTicket();
        new Thread(station,"张三").start();
        new Thread(station,"李四").start();
        new Thread(station,"王五").start();
    }



}
class BuyTicket implements Runnable{

    int ticket = 10;
    boolean flag = true;

    @Override
    public void run() {

        while (flag){

            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
    public  void buy() throws InterruptedException {
        if (ticket<=0){
            flag = false;
            return;
        }

            Thread.sleep(100);

        System.out.println(Thread.currentThread().getName()+"买到了第"+ticket--+"张票");
    }
}

加锁之后买票

java">/**
 * 同步块: synchronized (Obj ){ } Obj称之为同步监视器
 * Obj可以是任何对象﹐但是推荐使用共享资源作为同步监视器,也就是进行增删改的对象
 * 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this ,就是这个对象本身﹐或者是class
 *同步监视器的执行过程
     * 1.第一个线程访问,锁定同步监视器﹐执行其中代码.
     * 2.第二个线程访问﹐发现同步监视器被锁定﹐无法访问.
     * 3. 第一个线程访问完毕﹐解锁同步监视器.
     * 4.第二个线程访问,发现同步监视器没有锁﹐然后锁定并访问
 */
public class SafeTicket {
    public static void main(String[] args) {
        BuyTicket station = new BuyTicket();
        new Thread(station,"张三").start();
        new Thread(station,"李四").start();
        new Thread(station,"王五").start();
    }



}

class BuyTicket1 implements Runnable{

    int ticket = 10;
    boolean flag = true;

    @Override
    public void run() {

        while (flag){

            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
    public  synchronized void buy() throws InterruptedException {
        if (ticket<=0){
            flag = false;
            return;
        }

            Thread.sleep(100);

        System.out.println(Thread.currentThread().getName()+"买到了第"+ticket--+"张票");
    }
}

Lock的使用

java">/**
 * 从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当
 * java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。
 * 锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开
 * 始访问共享资源之前应先获得Lock对象
 * ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,
 * 在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释
 * 放锁。
 */
public class TestLock {
    public static void main(String[] args) {
        TestLock1 station = new TestLock1();
        new Thread(station, "张三").start();
        new Thread(station, "李四").start();
        new Thread(station, "王五").start();
    }
}
class TestLock1 implements Runnable {
    int ticket = 10;
    boolean flag = true;
    //    显式的定义锁
    private final ReentrantLock reentrantLock = new ReentrantLock();
    @Override
    public void run() {
        while (flag) {
            buy();
        }
    }
    public void buy() {
        try {
            reentrantLock.lock();//加锁
            if (ticket <= 0) {
                flag = false;
                return;
            }
            Thread.sleep(100);
            System.out.println(Thread.currentThread().getName() + "买到了第" + ticket-- + "张票");
        } catch (Exception e) {
        } finally {
            reentrantLock.unlock();//释放锁
        }
    }
}
/**
 * synchronized 与Lock 的对比
 * Lock是显式锁(手动开启和关闭锁,别忘记关闭锁) synchronized是隐式锁,出了作用域自动释放
 * Lock只有代码块锁,synchronized有代码块锁和方法锁
 * 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
 * 优先使用顺序:
 * Lock >同步代码块(已经进入了方法体,分配了相应资源)>同步方法(在方法体之外)
 */

生产者和消费者问题

管程法解决
java">/**
 * 生产者和消费者问题--管程法解决
 * 四个角色:生产者、消费者、产品、缓冲区
 * 生产者生产出产品后,通知消费者来取,消费者取出产品后,通知消费者来生产
 * 容器里面没有产品时,通知生产者生产,消费者等待
 * 容器中产品满时,通知消费者来取,生产者等待
 */
public class TestPc {

    public static void main(String[] args) {
        SynContain contain = new SynContain();
        new Product(contain).start();
        new Customer(contain).start();
    }
}

/**
 * 定义生产者
 */
class Product extends Thread {
private  SynContain contain ;

    public Product(SynContain contain) {
        this.contain = contain;
    }

    @Override
    public void run() {
        for (int i = 0; i < 13; i++) {
            System.out.println("生产了"+i+"个产品");
            contain.push(new Dog(i));
        }
    }
}

/**
 * 定义消费者
 */
class Customer extends Thread {
    private  SynContain contain ;
    public Customer(SynContain contain) {
        this.contain = contain;
    }

    @Override
    public void run() {
        for (int i = 0; i < 13; i++) {
            System.out.println("取出了第"+contain.pop().i+"个产品");
        }
    }
}

/**
 * 定义产品
 */
class Dog {
    int i;//产品编号,标识唯一产品

    public Dog(int i) {
        this.i = i;
    }
}


/**
 * 定义缓冲区
 */
class SynContain {
    Dog[] dogs = new Dog[10];//定义容器大小
    int count = 0;//计数器

    //    生产者放入产品
    public synchronized void push(Dog dog) {


        if (count == dogs.length) {
            //    如果容器满了,通知消费者来取,生产者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 如果没有满,生产者丢入产品,同时也同这消费者消费
        dogs[count] = dog;
        count++;
        this.notifyAll();


    }

    //    消费者消费产品
    public synchronized Dog pop() {


        if (count == 0) {
            //    如果容器中,没有产品,通知生产者生产,消费者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 消费者取出产品,痛着生产者生产
        count--;
        Dog dog = dogs[count];

        this.notifyAll();
        return dog;


    }

}
信号灯法解决
java">/**
 * 生产者和消费者问题--信号灯法解决
 * 四个角色:生产者、消费者、产品、缓冲区
 * 生产者生产出产品后,通知消费者来取,消费者取出产品后,通知消费者来生产
 * 容器里面没有产品时,通知生产者生产,消费者等待
 * 容器中产品满时,通知消费者来取,生产者等待
 */
public class TestPc2 {

    public static void main(String[] args) {
        TV tv = new TV();
        new Actor(tv).start();
        new Watcher(tv).start();
    }
}

/**
 * 定义生产者-->演员
 */
class Actor extends Thread {
    TV tv;

    public Actor(TV tv) {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i%2==0){
                tv.show("唱歌");
            }else {
                tv.show("游戏主播");
            }
        }
    }
}

/**
 * 定义消费者--->观众
 */
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 {
    //演员表演,观众等待T
//观众观看,演员等待F
    boolean flag = true;
    String voice;//表演的节目

    //节目表演
    public synchronized void show(String voice) {
//        没有节目,观众等待
        if (!flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
//        有节目,通知观众观看
        this.notifyAll();
        System.out.println("表演了:"+voice);
        this.voice = voice;
        this.flag = !this.flag;
    }


    //观看表演
    public synchronized void watch() {
//       观众观看,演员等待
        if (flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
//        没有节目,通知演员表演
        this.notifyAll();
        System.out.println("观看了:"+this.voice );
        this.flag = !this.flag;
    }
}

线程池

java">
/**
 * 线程池:
 * 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
 * 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。
 * 可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
 * 好处:
 * 提高响应速度(减少了创建新线程的时间)
 * 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
 * 便于线程管理
 */
public class TestPool {
    public static void main(String[] args) {

    //    创建服务,创建线程池
    //    newFixedThreadPool参数为:线程池大小
        ExecutorService service = Executors.newFixedThreadPool(10);
//        执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
//        关闭
        service.shutdown();
    }


}
class MyThread implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}


http://www.niftyadmin.cn/n/1617285.html

相关文章

《算法笔记》2.6小节——C/C++快速入门-函数

《算法笔记》2.6小节——C/C快速入门->函数 问题 A: 习题7-5 字符串逆序存放 [命题人 : 外部导入] 时间限制 : 1.000 sec 内存限制 : 12 MB 解决 : 1781 提交 : 2564 题目描述 写一个函数将一个字符串按反序存放。在主函数中输入一个字符串&#xff0c;通过调用该函数&…

spring cloud gateWay集成knife4j实现文档聚合

文章目录背景gateWay 介绍knife4j集成原因步骤创建swagger服务模块&#xff0c;用于其他业务模块&#xff0c;去调用。业务模块引入swagger服务模块网关模块聚合业务模块的swagger文档背景 gateWay 介绍 gateway相当于所有服务的门户&#xff0c;将客户端请求与服务端应用相分…

《算法笔记》2.7小节——C/C++快速入门-指针

《算法笔记》2.7小节——C/C快速入门->指针 问题 A: C语言10.1 [命题人 : 外部导入] 时间限制 : 1.000 sec 内存限制 : 32 MB 解决 : 1792 提交 : 2222 题目描述 输入a和b两个整数&#xff0c;按先大后小的顺序输出a和b。注意请使用指针变量的方式进行比较和输出。 输入 …

spring cloud feign实现服务的远程调用和负载均衡

项目结构介绍 test-service&#xff1a;作为服务的消费者 user-service&#xff1a;作为服务的提供者 common-service&#xff1a;主要用于为其他服务提供公共的方法以及依赖 author-service&#xff1a;权限认证服务&#xff0c;为其他服务模块提供权限的认证和授权 项目服务注…

《算法笔记》2.8小节——C/C++快速入门-结构体(struct)的使用

《算法笔记》2.8小节——C/C快速入门->结构体(struct)的使用 问题 A: C语言11.1 [命题人 : 外部导入] 时间限制 : 1.000 sec 内存限制 : 32 MB 题目描述 完成一个对候选人得票的统计程序。假设有3个候选人&#xff0c;名字分别为Li&#xff0c;Zhang和Fun。使用结构体存储…

《算法笔记》2.10小节——C/C++快速入门-黑盒测试

《算法笔记》2.10小节——C/C快速入门->黑盒测试 问题 A: AB 输入输出练习I [命题人 : 外部导入] 时间限制 : 1.234 sec 内存限制 : 32 MB 题目描述 你的任务是计算ab。这是为了acm初学者专门设计的题目。你肯定发现还有其他题目跟这道题的标题类似&#xff0c;这些问题也…

《算法笔记》3.1小节——入门模拟-简单模拟

《算法笔记》3.1小节——入门模拟->简单模拟 问题 A: 剩下的树 [命题人 : 外部导入] 时间限制 : 1.000 sec 内存限制 : 32 MB 题目描述 有一个长度为整数L(1<L<10000)的马路&#xff0c;可以想象成数轴上长度为L的一个线段&#xff0c;起点是坐标原点&#xff0c;在…

《算法笔记》3.2小节——入门模拟-查找元素

《算法笔记》3.2小节——入门模拟->查找元素 问题 A: 统计同成绩学生人数 [命题人 : 外部导入] 时间限制 : 1.000 sec 内存限制 : 32 MB 题目描述 读入N名学生的成绩&#xff0c;将获得某一给定分数的学生人数输出。 输入 测试输入包含若干测试用例&#xff0c;每个测试…