2022-08-03 第八小组 学习笔记

目录

synchronized

同步方法

处理实现runnable接口方式的线程安全问题

处理继承Thread类方式的线程安全问题

同步代码块

 案例——卖票

 死锁

java死锁产生的四个必要条件

volatile关键字的使用

如何防范死锁?

线程通信

线程的常用方法

Thread类中的方法

面试题

sleep和wait方法的区别


synchronized

有三种方式来加锁,分别是:

  1. 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
  2. 静态方法,作用于当前类对象加锁,进入同步代码前要获得的当前类对象的锁
  3. 代码块,指定加锁对象,对给定对象加锁,进入同步代码块之前要获得给定对象的锁
  • 实例方法:调用该方法的实例
  • 静态方法:类对象
  • this:调用该方法的实例对象(类对象   实例)
  • 类对象:类对象
public class Ch01 {

    public static void main(String[] args) {
        // 同步代码块
        // 创建一个对象
        // 类对象
        // 当前实例this
        // 同步监视器
     synchronized (Ch01.class) {
            int a = 1;
        }
    }
}

操作共享数据的代码

共享数据:多个线程共同操作的变量,都可以充当锁 

当使用同步方法时,synchronized锁的东西是this(默认的)

同步方法

有synchronized关键字修饰的方法。 由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。

注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类。

处理实现runnable接口方式的线程安全问题

synchronized(同步监视器){
    
    // 需要被同步的代码:操作共享数据的代码,或理解为多个线程共同操作的变量  
}

同步监视器:锁。任何类的对象都可以充当锁,但是多个线程必须要共用同一把锁

处理继承Thread类方式的线程安全问题

public static Object obj = new Object();

同步代码块

有synchronized关键字修饰的语句块。 被该关键字修饰的语句块会自动被加上内置锁,从而实现同步。

synchronized(object){ }

 案例——卖票

定义一个Ticket类。创建一个初始的票数,假如说是50张,然后重写run方法。创建一个死循环,让顾客一直买票。如果,票数等于0,输出卖空,然后跳出循环。每卖出一张票,count就--,然后获取当前对象的名称输出剩余的票数。如果这样输出就会发现,有问题,可能会有两个窗口同事都有50张票和会有负数票的情况,因为没加锁。

package night;

public class Ticket implements Runnable {
    private int count = 50;   

    @Override
    public void run() {

        while (true) {
            
                if (count == 0) {
                    System.out.println("卖完");
                    break;
                } else {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count--;
                    System.out.println(Thread.currentThread().getName() + "剩余:" + count);
                }          
        }
    }//end run
}//end Ticket

加锁:

package night;

public class Ticket implements Runnable {
    private int count = 50;
    private Object Lock = new Object();

    @Override
    public void run() {


        while (true) {
            synchronized (Lock) {

                if (count == 0) {
                    System.out.println("卖完");
                    break;
                } else {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count--;
                    System.out.println(Thread.currentThread().getName() + "剩余:" + count);
                }

            }
        }
    }//end run
}//end Ticket

输出:

package night;

public class Demo {
    public static void main(String[] args) {
        Ticket ticket=new Ticket();
        Thread t1=new Thread(ticket,"窗口一");
        t1.start();
        Thread t2=new Thread(ticket,"窗口二");
        t2.start();
        Thread t3=new Thread(ticket,"窗口三");
        t3.start();
    }
    private static void demo(){

    }
}

 死锁

多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

java死锁产生的四个必要条件

1、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
3、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
4、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。
原文链接:https://blog.csdn.net/qq_31762741/article/details/123274086

当上述四个条件都成立的时候,便形成死锁。当然,死锁的情况下如果打破上述任何一个条件,便可让死锁消失。

public class DeadLock {

    //创建两个对象
    static Object a = new Object();
    static Object b = new Object();

    public static void main(String[] args) {
        new Thread(()->{
            synchronized (a) {
                System.out.println(Thread.currentThread().getName()+" 持有锁a,试图获取锁b");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (b) {
                    System.out.println(Thread.currentThread().getName()+" 获取锁b");
                }
            }
        },"A").start();

        new Thread(()->{
            synchronized (b) {
                System.out.println(Thread.currentThread().getName()+" 持有锁b,试图获取锁a");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (a) {
                    System.out.println(Thread.currentThread().getName()+" 获取锁a");
                }
            }
        },"B").start();
    }
}

volatile关键字的使用

主要作用:是使变量在多个线程间可见。

强制用公共堆栈中取的变量的值,而不是从线程私有的数据栈中取得变量的值。

如何防范死锁?

1.尽量避免使用多个锁。

2.规范的使用多个锁,并设计好锁的获取顺序。

3.随用随放。即是,手里有锁,如果还要获得别的锁,必须释放全部资源才能各取所需。

4.规范好循环等待条件。比如,使用超时循环等待,提高程序可控性

线程通信

多个线程的使用

/*
线程通信
A先走,B等待,A走完唤醒B ,B走
*/

线程的常用方法

Thread类中的方法

1.start:启动当前线程;执行run方法

2.run

3.currentThread:静态方法,获取当前正在执行的线程

4.getId():返回此线程的唯一标识

5.setName(String):设置当前线程的name

6.getName():获取当前线程的name

7.getPriority():获取当前线程的优先级

8.setPriority(int):设置当前线程的优先级

9.getState():获取当前线程的声明周期

10.interrupt():中断线程的执行

11.interrupted():查看当前线程是否中断

面试题

sleep和wait方法的区别

sleep 方法和 wait 方法都可以用来放弃 CPU 一定的时间

不同点:在于如果线程持有某个对象的监视器,sleep 方法不会放弃这个对象的监视器,wait 方法会放弃这个对象的监视器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值