Java线程进阶

本文介绍了Java中死锁的概念和避免策略,通过实例展示了死锁的情况以及如何通过设置锁的超时和休眠时间来防止死锁。此外,文章还讲解了线程间的通信,使用wait和notify实现线程交替执行。接着讨论了线程状态,并解释了线程池的工作原理和创建线程池的方法。最后,对比了synchronized和Lock锁的区别,并展示了Lock手动锁的使用示例。
摘要由CSDN通过智能技术生成

上节线程基础回顾:

 目录:

1.1死锁,如何避免死锁?

1.2线程之间的通信

1.3线程状态

1.4线程池

1.5Lock手动锁

技术细节:

1.1死锁,如何避免死锁?

说明:

奥特曼拥有A锁等待B锁释放,怪兽拥有B锁等待A锁释放,他们相互等待就会形成死循环,造成阻塞,死锁现象

举例:

李某和范某吃饭,饭店活动,每人只有一只筷子,一双筷子才能吃饭

代码演示:

package demo09;

public class BoyRunnable implements Runnable {
    @Override
    public void run() {
synchronized (LockObject.lockB){
    System.out.println("李某获取筷子B");
    synchronized (LockObject.lockA){
        System.out.println("李某获取筷子A");
        System.out.println("李某可以吃饭了");
    }
}
    }
}

------------------------------
package demo09;

public class GirlRunnable implements Runnable{
    @Override
    public void run() {
            //锁嵌套
        synchronized (LockObject.lockA){
            System.out.println("范某获得一个筷子A");
            synchronized (LockObject.lockB){
                System.out.println("范某获得另外一个筷子B");
                System.out.println("范某可以吃饭了");
            }
        }
    }
}
---------------------
package demo09;

public class LockObject {
    public static Object lockA = new Object();//A筷子
    public static Object lockB = new Object();//B筷子
}
---------------------
package demo09;

public class Test {
    public static void main(String[] args) {
        BoyRunnable boy = new BoyRunnable();
        Thread t1 = new Thread(boy);

        GirlRunnable girl = new GirlRunnable();
        Thread t2 = new Thread(girl);

        t1.start();
        t2.start();

    }
}



输出结果:

 总结:

他们都没有吃上饭,他们之间互相等待,所以出现了这种死锁现象

那么如何避免呢?

1.尽量不要出现锁嵌套

2.尽量不使用锁,使用安全类。java.util.concurrent下的类,都属于安全类

3.设置锁的超时时间,Lock锁;到达指定时间拿不到另外一个锁资源,则不会接着等待,抛出异常

4.设置锁休眠时间

代码演示:

package demo09;

public class BoyRunnable implements Runnable {
    @Override
    public void run() {
 //设置休眠时间100单位毫秒
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
synchronized (LockObject.lockB){
    System.out.println("李某获取筷子B");
    synchronized (LockObject.lockA){
        System.out.println("李某获取筷子A");
        System.out.println("李某可以吃饭了");
    }
}
    }
}

------------------------------
package demo09;

public class GirlRunnable implements Runnable{
    @Override
    public void run() {
            //锁嵌套
        synchronized (LockObject.lockA){
            System.out.println("范某获得一个筷子A");
            synchronized (LockObject.lockB){
                System.out.println("范某获得另外一个筷子B");
                System.out.println("范某可以吃饭了");
            }
        }
    }
}
---------------------
package demo09;

public class LockObject {
    public static Object lockA = new Object();//A筷子
    public static Object lockB = new Object();//B筷子
}
---------------------
package demo09;

public class Test {
    public static void main(String[] args) {
        BoyRunnable boy = new BoyRunnable();
        Thread t1 = new Thread(boy);

        GirlRunnable girl = new GirlRunnable();
        Thread t2 = new Thread(girl);

        t1.start();
        t2.start();

    }
}

打印结果:

 1.2线程之间的通信

说明:

* 线程交替执行;做到先存钱在取钱;先加后减等等

代码演示:

package demo10;

public class BankCard {
    //余额属性
    private double blance;
    //true表示有钱,false表示没钱
    private boolean flag;
   //存钱方法
    public synchronized void save(double money){
        //如果卡里有钱存线程就会进入等待队列。调用wait方法,调用wait方法;这里的wait方法是属于object类里的
        if(flag){
            try {
                //调用wait方法会有异常出现
                wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        blance = blance + money;
        System.out.println(Thread.currentThread().getName()+"往卡中存入1000,卡中余额:"+blance+"元");
        flag = true;
        //唤醒等待队列的线程
        // notify也是object类中的方法
        notify();

    }
    //取钱方法
    public synchronized void take(double money){
    //如果卡里没有钱取线程就会进入等待队列。调用wait方法,调用wait方法;这里的wait方法是属于object类里的
        if(flag==false){
            try {
                //调用wait方法会有异常出现
                wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        blance = blance-money;
        System.out.println(Thread.currentThread().getName()+"从卡中取出1000,卡中余额:"+blance+"元");
        flag = false;
        //唤醒等待队列的线程
        // notify也是object类中的方法
        notify();
    }

}
------------------------------------------
package demo10;

public class SaveRunable implements Runnable{
    //这里不能new,new的话他们存钱和取钱就不是一张卡了
    private BankCard bankCard;
    //构造方法,传进来共同的一个卡对象
    public SaveRunable(BankCard card){
    bankCard = card;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            bankCard.save(1000);
        }
    }
}
------------------------------------------
package demo10;

public class TakeRunnable implements Runnable {
    //这里不能new,new的话他们存钱和取钱就不是一张卡了
    private BankCard bankCard;
    //构造方法,传进来共同的一个卡对象
  public TakeRunnable(BankCard card){
      bankCard = card;
  }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            bankCard.take(1000);
        }
    }
}
------------------------------------------
package demo10;

public class Test01 {
    public static void main(String[] args) {
        BankCard bankCard = new BankCard();

        SaveRunable saveRunable = new SaveRunable(bankCard);//任务存
        TakeRunnable takeRunnable = new TakeRunnable(bankCard);//任务取

        Thread t1 = new Thread(saveRunable,"孙悟空");//t1线程执行存钱任务
        Thread t2 = new Thread(takeRunnable,"张飞");//t2线程执行取钱任务

        t1.start();
        t2.start();
    }
}

打印结果:

 拓展:

*wait   sleep 他们有什么区别

1.wait 需要使用notify或notifyAll唤醒,而sleep到时间自动唤醒

2.wait 来自于Object类中,sleep 来自Thread类中

3.wait 会释放锁资源,sleep不会释放锁资源:sleep释放锁资源必须等到休眠时间结束才能释放

4.wait 必须放在同步代码中,而sleep可以放在任意位置

1.3线程状态

 图示:

 1.4线程池

概念:

 原理:

 *将任务提交给线程池,由线程池分配任务,运行任务,并在当前任务结束后复用线程

 创建线程池:

细节:

* Executor :( 线程池对象 ) 它是线程池的顶级接口,该接口中就存在一个方法;void execute(Runnable command):执行线程任务的方法
* ExecutorService : 它是Executor接口的子接口
* ExecutorService中的方法
* 1.shutdown():关闭线程池---如果当前线程池还有任务,需要等任务完成后才会关闭
* 2.shutdownNow():立刻关闭线程池
* 3.isShutdown():是否属于关闭状态。返回类型是一个boolean值
* 4.isTerminated():判断是否线程池终止了
* 5.submit( Callable<T>   task ):执行线程任务的方法
*   submit( Runnable task )
* 6.Executors : 它是线程池的工厂类,该类可以获取线程池对象

代码演示:

package demo11;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Executor :( 线程池对象 ) 它是线程池的顶级接口,该接口中就存在一个方法;void execute(Runnable command):执行线程任务的方法
 * ExecutorService : 它是Executor接口的子接口
 * ExecutorService中的方法
 * 1.shutdown():关闭线程池---如果当前线程池还有任务,需要等任务完成后才会关闭
 * 2.shutdownNow():立刻关闭线程池
 * 3.isShutdown():是否属于关闭状态。返回类型是一个boolean值
 * 4.isTerminated():判断是否线程池终止了
 * 5.submit( Callable<T>   task ):执行线程任务的方法
 *   submit( Runnable task )
 * 6.Executors : 它是线程池的工厂类,该类可以获取线程池对象
 */
public class Test01 {
    public static void main(String[] args) {
        //newFixedThreadPool创建一个固定个数的线程池对象---alibaba不建议使用Executors创建线程池
//        ExecutorService executorService = Executors.newFixedThreadPool(5);
        //创建单一线程池---池子中只有一个线程的对象----适合任务的有序执行
//        ExecutorService executorService = Executors.newSingleThreadExecutor();
        //可变线程池---根据任务的多少自己变化
        ExecutorService executorService = Executors.newCachedThreadPool();

        for (int i = 0; i < 6; i++) {
            //submit 在这里也可以使用  ;execute
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"~~~~~~~~~~~~~~~~~~~~~");
                }
            });
        }
           //关闭线程池
          //executorService.shutdownNow();
    }
}
package demo11;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Test02 {
    public static void main(String[] args) {
        //延迟线程池
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
        for (int i = 0; i < 100; i++) {
            executorService.schedule(new Runnable() {

                @Override
                public void run() {
                    System.out.println("定时执行任务");
                }
                //TimeUnit.SECONDS  单位秒
            },5, TimeUnit.SECONDS);
        }
    }
}
package demo11;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Test03 {
    public static void main(String[] args) {
        //alibaba 建议自己new线程池----构造函数报错原因
        //1.倒包  2.没有无参构造函数  3.构造函数私有化
        BlockingQueue blockingQueue = new ArrayBlockingQueue(3);//允许等待的个数

        //        ThreadPoolExecutor();中的参数
        //        int corePoolSize,核心线程数
        //        int maximumPoolSize,最大线程数
        //        long keepAliveTime,等待时间
        //        TimeUnit unit,时间单位
        //        BlockingQueue<Runnable> workQueue:等待队列,等待队列中的个数

        ThreadPoolExecutor executorService = new ThreadPoolExecutor(2, 5, 10, TimeUnit.SECONDS, blockingQueue);

        for (int i = 0; i < 8; i++) {
            executorService.submit(new Runnable() {

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

拓展:

1.java层的顶层类

object

2.集合的顶层类

collection:单列集合类的根接口

map:键值对集合的根接口

3.异常的顶层类

Throwable

他的下面有两个子类:--Exception

                                       --Error

 1.5Lock手动锁

 代码演示:

package demo04;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TicketTask implements Runnable{
    private int ticket = 100;
    //创建一个手动锁
    private Lock l = new ReentrantLock();
    @Override
    public void run() {
        while(true) {
            l.lock();
            try { if (ticket>0){
                ticket--;
                System.out.println(Thread.currentThread().getName() + " 卖了一张票;剩余: " + ticket+"张");
            }else {
                break;
            }
            }finally {
                //解锁
                //不加上解锁,哪个窗口抢到,哪个窗口直接卖完100张
                l.unlock();
            }
        }

    }
}

---------------------
package demo04;

public class Test04 {
    public static void main(String[] args) {
        TicketTask task = new TicketTask();
        Thread t1 = new Thread(task,"窗口A");
        Thread t2 = new Thread(task,"窗口B");
        Thread t3 = new Thread(task,"窗口C");
        Thread t4 = new Thread(task,"窗口D");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}
synchronized自动锁------Lock手动锁的区别

1.   lock手动锁只能给代码块上

2.   synchronized自动上锁解锁,lock手动上锁解锁

3.   synchronized能在方法和代码块上,上锁。lock只能在代码块上,上锁

4.   lock 可以知道是否获得锁资源

问题小结:

 总结:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值