线程进程2--线程安全-死锁

5. Thread类中常用的一些方法

static void sleep:使当前线程阻塞多少毫秒--线程休眠

yield:当前线程让出cpu参与下次竞争--使用yield线程出现交换执行的频率变高了

join加入当前线程上(插入的线程执行完毕后,当前的线程才会执行)

setDaemon()设置线程为守护线程(当所有的线程执行完毕后,守护线程也会终止)

// sleep--阻塞多少时间
public class Method extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++){
            System.out.println(Thread.currentThread().getName()+"-->"+i);
            //休眠1秒--每隔一秒执行
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("------------------");
        }
    }
}

//join--等待线程
    public static void main(String[] args) throws InterruptedException {
        Method thread1 = new Method();
        thread1.setName("线程1");
        Method thread2 = new Method();
        thread2.setName("线程2");
        thread1.start();
        thread2.start();

        thread1.join();// 加入到主线程上--线程1执行完之后再执行main线程.如果主线程需要等待1和2线程的执行结果 做下一步操作时。
        //thread2.join();
        for (int i = 0; i < 20; i++) {
            //Thread.sleep(10);
            System.out.println("main线程" + i);
        }
-------------------------------------------------------------------------------
//setDaemon--守护线程
        Method thread1 = new Method();
        thread1.setName("线程1");
        thread1.setDaemon(true);//设置thread1为守护线程--以主线程为主,当主线程停息的时候守护线程会出现,主线程完毕之前,守护线程也会终止。
        //比如下面的for循环,虽然i打印输出了但是它还要进行判断和i++,在执行判断这个过程时,守护线程就排上了用场
        thread1.start();
     for (int i = 0; i < 20; i++) {
            //Thread.sleep(10);
            System.out.println("main线程" + i);
        }

        thread1.join();// 加入到主线程上--线程1执行完之后再执行main线程.如果主线程需要等待1和2线程的执行结果 做下一步操作时。
        //thread2.join();
        for (int i = 0; i < 20; i++) {
            //Thread.sleep(10);
            System.out.println("main线程" + i);
        }
-------------------------------------------------------------------------------
         Method thread1 = new Method();
        thread1.setName("线程1");
        thread1.setDaemon(true);//设置thread1为守护线程--以主线程为主,当主线程停息的时候守护线程会出现,主线程完毕之前,守护线程也会终止。
        //比如下面的for循环,虽然i打印输出了但是它还要进行判断和i++,在执行判断这个过程时,守护线程就排上了用场
        thread1.start();
     for (int i = 0; i < 20; i++) {
            //Thread.sleep(10);
            System.out.println("main线程" + i);
        }

6. 解决线程安全问题

6.1什么情况下会出现线程安全问题

当多个线程操作同一个资源时,则会出现线程安全问题

6.2java如何解决线程安全问题

第一种方式:使用synchronized自动锁

第二种方式:使用Lock手动锁

使用锁相对于把原来异步转换同步操作。

第一种:使用synchroonized关键字解决

它可以使用在方法上,也可以使用在代码块中

synchroinzed(共享锁对象){

同步代码块。

}

public class MyRunnableAL implements Runnable{
    private int tick = 100;
    
    private Object obj = new Object();
    
    @Override
    public void run() {
        while (true){
            //使用在代码块中
            synchronized (obj){
                if (tick>0){
                    tick--;
                    System.out.println(Thread.currentThread().getName()+" tick:"+tick);
                }else {
                    break;
                }
            }
        }
    }
}
-----------------------------------------------------------------------------
   private int tick = 100;

    //使用在方法上
    @Override
    public void run() {
        while (true){
            setTick();
            if (tick<=0){
                break;
            }
        }
    }

    public synchronized void setTick(){
        if (tick>0){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            tick--;
            System.out.println(Thread.currentThread().getName()+" tick:"+tick);
        }
    }

-------------------------------------------------------------------------------
public class Test02 {
    public static void main(String[] args) {
        MyRunnableAL myRunnableAL1 = new MyRunnableAL();
        new Thread(myRunnableAL1,"A").start();

        new Thread(myRunnableAL1,"B").start();

        new Thread(myRunnableAL1,"C").start();

        new Thread(myRunnableAL1,"D").start();
    }
}

第二种:使用Lock手动

Lock是一个接口,实现类ReentrantLock

private int tick = 100;
    //手动加锁Lock
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                //加锁
                lock.lock();
                if (tick > 0) {
                    tick--;
                    System.out.println("当前线程对象的名字为:" + Thread.currentThread().getName() + "----剩余:" + tick + "张票");
                } else {
                    break;
                }
            } finally {//确保解锁---防止死锁---finally语句块:无论怎样都会执行的语句块
                //解锁
                lock.unlock();
            }
        }
    }

6.3synchroinzed自动锁和Lock手动锁的区别

synchroinzed可以使用在方法上和代码块中,自动加锁和释放锁,不会出现锁死现象。

Lock只能用在代码块中,需要手动加锁和释放锁,如果不释放锁,会被锁死

比较灵活释放锁放在finally代码块中。

7. 线程死锁问题

线程A拥有所资源a,希望获取到锁资源b,线程B拥有资源b,希望获取锁资源a。

两个线程互相拥有对方希望获取的锁资源。可能会出现程序堵塞。从而造成死锁。

解决:

1.不要使用锁嵌套。

2.设置超时时间。--Lock类中tryLock

3.使用安全java.until.controller下的类。

8. 线程通信

wait方法(等待线程)notify方法(唤醒线程)--必须用在synchronized自动锁

package com.wjy.waitAndNotify;

public class BankCard {
    private double balance;

    private boolean flag;//true:有钱  false:没钱

    //存款--如果有钱先取走(线程等待)
    public synchronized void cun(double money){
        if (flag==true){
            try {
            //属于Object类的方法。进入等待队列  并释放拥有的锁
                wait();//线程等待
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        //如果没钱,则存款
        balance+=money;
        System.out.println(Thread.currentThread().getName()+"存钱成功,余额为:"+balance);
        //设置有钱标志--代表可以取钱
        flag=true;
        //唤醒线程--唤醒等待队列中的某个线程
        notify();
    }

    //取款--如果没钱先线程等待
    public synchronized void qu(double money){
        //没钱--先等待
        if (flag==false){
            try {
                wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        //有钱--取钱
        balance-=money;
        System.out.println(Thread.currentThread().getName()+"取钱成功,余额为:"+balance);
        //设置没钱标志--代表可以存钱
        flag=false;
        notify();
    }

}
package com.wjy.waitAndNotify;

public class CunThread extends Thread{
    private BankCard bankCard;
    //构造方法--确保都是使用的同一张卡
    public CunThread(BankCard bankCard){
        this.bankCard=bankCard;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            bankCard.cun(300);
        }
    }
}
package com.wjy.waitAndNotify;

public class QuThread extends Thread{
    private BankCard bankCard;
    public QuThread(BankCard bankCard){
        this.bankCard=bankCard;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++){
            bankCard.qu(300);
        }
    }
}
package com.wjy.waitAndNotify;

public class Test01 {
    public static void main(String[] args) {
        BankCard bankCard = new BankCard();
        CunThread cunThread = new CunThread(bankCard);
        cunThread.setName("c");
        QuThread quThread = new QuThread(bankCard);
        quThread.setName("q");
        cunThread.start();
        quThread.start();
    }
}

9. 线程池

new:新建状态

runnable:就绪状态和运行状态

blocked:堵塞状态

waiting:等待状态

timed_waiting:时间等待

terminated:终止

通过调用不同的方法相互转换

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值