线程学习5

回顾:

线程创建方式:

1.继承Thread
2.实现Runnable接口
3.实现Callable接口(有线程的返回值)

线程常用方法和属性:

  • start() run()
  • sleep 休眠
  • join 等待一个线程执行完成
  • interrupt 终止线程
  • yield 让出CPU的执行权

属性:
优先级
线程分组

TIMED_WAITING:有明确结束等待时间

线程安全问题

线程不安全的五个因素:

1.CPU抢占式执行
2.内存可见性 volatile可解决
3.指令重排序(编译器优化) volatile可解决
4.原子性 加锁可解决
5.多个线程同时修改了同一个变量

线程安全问题解决方案:

1.锁(让多线程排队执行)
2.使用私有变量

锁:
1.synchronized
2.Lock 手动锁

synchronized

实现原理:
三种使用场景:
1.6性能优化(四种状态)

synchronized和volatile 和Lock的区别

死锁:

定义:在多线程编程中(两个或两个以上的线程)因为资源抢占而造成线程无限等待的问题
在这里插入图片描述
线程和锁的关系:一对多
一个线程可以拥有多把锁,而一把锁只能被一个线程拥有。
死锁代码:

public class ThreadMain36 {
    public static void main(String[] args) {
        //锁A(资源A)
        Object lockA=new Object();
        //锁B(资源B)
        Object lockB=new Object();
        //线程1
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                //得到锁A
                synchronized (lockA){
                    System.out.println("线程1:获取了锁A");
                    try {
                        //休眠1s,让线程2先得到锁B
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程1:等待获取锁B");
                    //尝试获取资源B
                    synchronized (lockB){
                        System.out.println("线程1:得到了锁B");
                    }
                }
            }
        }, "t1");
        t1.start();
        //创建并启动线程2
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                //得到锁B
                synchronized (lockB){
                    System.out.println("线程2:获得了锁B");
                    try {
                        //休眠1s,让线程取到了锁B
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程2:等待获取锁A");
                    //尝试获取资源A
                    synchronized (lockA){
                        System.out.println("线程2:得到了锁A");
                    }
                }
            }
        },"t2");
    t2.start();
    }
}

死锁操作的四个条件(同时满足):

  • 1.互斥条件(一个资源只能被一个线程持有,当被一个线程持有之后就不能被其他线程持有)--------不可改变
  • 2.请求拥有条件(一个线程持有了一个资源之后又试图请求另一个资源)-------可被修改
  • 3.不可剥夺条件(一个资源被一个线程拥有之后,如果这个线程不释放此资源,那么其他线程不能强制获得此资源)-----不可修改
  • 4.环路等待条件(多个线程在获取资源时形成了一个环形链)—可以修改

排查死锁的工具:

JDK的bin目录下
1.jconsole.exe
2.jvisualvm.exe
3.imc.exe—>启动jmx控制台----->线程----->死锁检测

如何解决死锁问题:

  • 请求拥有条件
  • 环路等待:最容易实现
    解决死锁可以通过控制获取锁的顺序来解决(破坏了环路等待条件)
    sleep休眠缺点:必须有明确的结束数据。

线程通讯机制:

定义:一个线程的动作可以被另一个线程感知到就叫线程通讯。

wait(休眠)/notify(唤醒)/ nofifyAll(唤醒全部)

wait为什么要加锁:

wait在使用的时候需要释放锁,在释放之前必须要有一把琐,所以要加锁

wait为什么要释放锁:

wait默认是不传任何值的,当不传递任何值的时候表示永久等待,这样就会造成一把锁被一个线程一直持有。为了避免这种问题的方式,所以在使用wait时一定要释放锁

wait /notify / nofifyAll 使用注意事项:

1.在使用以上方法时一定要加锁
2.加锁对象和 wait /notify / nofifyAll 的对象必须保持一致
3.一组 wait / notify / nofifyAll 必须是同一对象
4.nofifyAll只能唤醒当前对象的所有等待线程
wait () :void 不传参时表示传的是0-----永久等待
wait(long):void 毫秒级别的最大等待时间
wait(long,int):void 纳秒级别的最大等待时间

Thread.sleep(0) 和 Object.lock(0)的区别

1.sleep是Thread的静态方法,而lock 是Object的方法
2.sleep(0)立即触发一次CPU资源的抢占,而lock(0) 永久的等待下去

wait和sleep的区别

相同点:

  • 1.都可以让当前线程休眠
  • 2.都必须处理一个Interrupt 异常

不同点:

  • 1.sleep是Thread的静态方法,wait来自于Object中的一个方法
  • 2.传参不同。wait可以没有参数,sleep必须有一个大于等于0的参数
  • 3.wait 使用必须加锁,sleep使用时不用加锁
  • 4.wait使用时会释放锁,而sleep不会释放锁
  • 5.wait默认不传参的情况下会进入WAITING状态,而sleep会进入TIMED_WAITING状态

为什么wait会放到Object中而不是Thread?

wait必须要加锁和释放锁,而锁又是属于对象级别而非线程级别(线程和锁是一对多的关系,也就是一个线程可以拥有多把锁),为了灵活起见(一个线程之中会有多把锁),就把wait放在Object。

wait和LockSupport的区别:

相同:

  • 1.两个都可以进行休眠
  • 2.两者都可以传参或者不传参,并且二者线程状态也是一致的

不同:

  • 1.wait必须配合synchronized一起使用(必须加锁),而LockSupport不需要加锁。
  • 2.wait只能唤醒全部和随机一个线程,而LockSupport可以唤醒指定线程。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值