多线程复习

title: 多线程复习

date: 2020/10/17

1. 线程的生命周期及状态

新生、就绪、运行、阻塞、死亡

2. sleep、wait、join、yield

3. 对线程安全的理解

例子1:假如有10张票,三个人同时购买,由于没有线程同步,则他们有可能买到同一张票,当只剩最后一张票时,有可能两个人甚至三个人都抢到了最后一张票,则有可能出现负数的票

例子2:你的银行账户有100块,你和媳妇一起取钱,一个人取50块,另一个人取100块,可能会造成银行亏钱

例子3:list集合在添加数值时,两个线程在同一时刻将数值添加到了同一位置,会造成元素数量减少

解决:synchronized(队列+锁)关键字

1.同步方法

2.同步块:synchronized(Obj){},监视的对象obj是需要进行增删改查的对象

原理:synchronized方法控制对“对象”的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行

4. Thread和Runnable

5. 说说你对守护线程的理解

线程分为用户线程和守护线程

虚拟机必须确保用户执行完毕

用户线程执行完毕后,虚拟机不用等待守护线程执行完毕,会立即停止

如后台记录操作日志,监控内存,垃圾回收等待

thread.setDaemon(true); //默认是false表示用户线程,正常的线程都是用户线程

6. Thread Local的原理及使用场景

7. Thread Local内存泄漏问题,如何避免

8. 并发、并行、串行

9. 并发的三大特性

10. 为什么使用线程池,参数解释

11. 线程池线程复用的原理

12. 创建线程的两种方式

12.1 实现Thread类

//创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
public class TsetThread1 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("我在看代码---"+i);
        }

    }

    public static void main(String[] args) {
        //main线程,主线程

        //1.创建一个线程对象
        TsetThread1 tsetThread1 = new TsetThread1();

        //2.调用start()方法开启线程
        tsetThread1.start();


        for (int i = 0; i < 200; i++) {
            System.out.println("我在学习多线程--"+i);
        }
    }
}

12.2 实现Runnable

//创建线程方式二:实现Runnable接口,重写run()方法,调用start开启线程
public class TsetThread2 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("我在看代码---"+i);
        }

    }
    public static void main(String[] args) {
        //main线程,主线程

        //1.创建一个线程对象
        TsetThread2 tsetThread2 = new TsetThread2();
        //创建线程对象,通过线程对象来开启线程,代理
        Thread thread = new Thread(tsetThread2);
        thread.start();


        for (int i = 0; i < 200; i++) {
            System.out.println("我在学习多线程--"+i);
        }
    }
}

13. Lambda表达式

  • lambda表达式只能有一行代码的情况下才能简化成为一行,如果有多行,那么就用代码块包裹
  • 前提是接口为函数式接口(只有一个方法)
  • 多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号
public class TsetLambda {


    public static void main(String[] args) {
        Ilove love = null;

        love = (a, b, c) -> {
            System.out.println("I Love you-->"+a+b+c);
        };

        love.love(1,2,3);
    }

}

interface Ilove{
    void love(int a,int b,int c);
}

简化流程;普通接口实现类->静态内部类->局部内部类->匿名内部类->lambda表达式

14. sleep

thread.sleep(100)  //停止100ms
  • sleep指定当前线程阻塞的毫秒数
  • sleep存在异常InterruptedException
  • sleep时间达到后线程进入就绪状态
  • sleep可以模拟网络延时,倒计时等
  • 每一个对象都有一个锁,sleep不会释放锁

15. yield(线程礼让)

礼让线程,让当前正在执行的线程暂停,但不阻塞

将线程从运行状态转为就绪状态

让CPU重新调度,礼让不一定成功,看CPU心情

public class TsetYield{
    public static void main(String[] args) {
        MyYield myYield = new MyYield();

        new Thread(myYield,"a").start();
        new Thread(myYield,"b").start();
    }
}

class MyYield implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield();//礼让
        System.out.println(Thread.currentThread().getName()+"线程停止执行");
    }
}

16. Join

join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞

public class TestJoin implements Runnable{

    @Override
    public void run() {

        for (int i = 0; i < 1000; i++) {
            System.out.println("线程vip来了"+i);
        }
    }
    public static void main(String[] args) throws InterruptedException {
        //启动TestJoin的线程
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();

        //启动主线程
        for (int i = 0; i < 500; i++) {
            if(i==200){
                thread.join();//插队
            }
            System.out.println("main"+i);
        }
    }
}

17. 线程优先级

Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行

线程的优先级用数字表示,范围从1-10

Thread.MIN_PRIOPRITY = 1;

Thread.MAX_PRIOPRITY = 10;

Thread.NORM_PRIOPRITY = 5;

使用以下方式改变或获取优先级

getPriority().setPriority(int ×××)

18.线程不安全的例子

18.1 不安全买票

public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();

        Thread thread1 = new Thread(buyTicket,"我");
        Thread thread2 = new Thread(buyTicket,"你");
        Thread thread3 = new Thread(buyTicket,"黄牛");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}
class BuyTicket implements Runnable{

    //票的数量
    private int ticketNums = 10;
    //外部停止方式
    boolean flag = true;

    @Override
    public void run() {
        //买票
        while(flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    //买票的方法,synchronized同步方法,锁的是this
    private synchronized void buy() throws InterruptedException {
        //判断是否有票
        if (ticketNums<=0) {
            flag = false;
            return;
        }else {
            //模拟延时
            Thread.sleep(100);

            System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
        }
        }
    }

18.2 不安全取钱

public class UnsafeBank {

    public static void main(String[] args) {
        Account account = new Account(100, "结婚基金");
        Drawing you = new Drawing(account, 50, "你");
        Drawing girlfriend = new Drawing(account, 100, "girlfriend");

        you.start();
        girlfriend.start();

    }
}
//账户
class Account{
    int money; //余额
    String name; //卡名


    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

//银行
class Drawing extends Thread{
    Account account;//账户
    int drawingMoney;//取了多少钱
    int nowMoney; //你手里的钱

    public Drawing(Account account,int drawingMoney,String name){
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;

    }
    //取钱
    @Override
    public  void run() {

        //锁的对象是变化的量,需要增删改的对象
        synchronized (account){
            //判断有没有钱
            if(account.money-drawingMoney<0){
                System.out.println(Thread.currentThread().getName()+"钱不够,取不了!");
                return;
            }

            //模拟延时
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //卡内余额
            account.money = account.money-drawingMoney;
            //你手里的钱
            nowMoney = nowMoney + drawingMoney;

            System.out.println(account.name+"的余额为:"+account.money);
            System.out.println(Thread.currentThread().getName()+"手里的钱:"+nowMoney);
        }

    }
}

18.3 不安全集合

public class UnsafeList {
    public static void main(String[] args) throws InterruptedException {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                synchronized (list){
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }
        Thread.sleep(3000);

        System.out.println(list.size());
    }
}

19. 锁

19.1 死锁

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形,某一个同步块同时拥有“两个以上的对象的锁”时,就可能会发生“死锁”的问题

19.2 Lock锁

private final ReentrantLock lock = new ReentranLock();
try{
  lock.lock();//加锁
    ...
}finally{
    //解锁
    lock.unlock();
}

20. wait()

作用:表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁

wait(long timeout):指定等待的毫秒数

21. 线程池

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.shutdown();
    }
}

class MyThread implements Runnable{

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

ExecutorService:真正的线程池接口

void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable

void shutdown():关闭连接池

Executors:工具类、线程池的工程类,用于创建并返回不同的线程池

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]和\[2\]提供了一些关于Java多线程面试题。这些面试题可以帮助你在面试复习并提高你的理论知识。其中,引用\[1\]展示了两种创建多线程的方式,一种是继承Thread类,另一种是实现Runnable接口。继承Thread类的方式比较简单,但是限制了类的继承关系,而实现Runnable接口的方式更加灵活。引用\[3\]展示了使用Callable和FutureTask的方式创建多线程,并获取线程执行结果的示例。这种方式可以在多线程执行完毕后获取线程的返回结果。希望这些面试题能够帮助你在面试中展示你的多线程知识和实战能力。 #### 引用[.reference_title] - *1* *3* [java2023多线程面试题](https://blog.csdn.net/weixin_68009402/article/details/130399986)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Java面试题及答案整理汇总(2023最新版)](https://blog.csdn.net/Design407/article/details/129009269)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值