多线程总结

1、线程

线程(Thread):线程是进程中最小的调度的单元(单位),cpu控制的最小的执行单元。轻量级的进程。任何一个程序都至少有一个线程在用,多个线程共享内存。多线程切换消耗的资源少。

并发:在同一时间间隔内,同时有多个线程运行。

并行:在同一时刻,同时有多个线程运行。

2、如何创建线程

1、继承Thread类 :受限于java单继承

        重写run方法,创建线程对象,启动线程(start())

2、实现Runnable接口:(常用) 继承类,实现多个接口,实现资源的共享;推荐使用

         实现接口,重写run方法

3、实现Callable接口:可以获取线程的返回值

public class MyCall implements Callable<String> {
    @Override
    public String call() throws Exception {
        System.out.println("线程在执行的任务");
        return Thread.currentThread().getName();
    }

    public static void main(String[] args) {
//        创建Callable接口的实例
        MyCall myCall=new MyCall();
//       FutureTask 实例
        FutureTask<String> task=new FutureTask<>(myCall);
        Thread thread=new Thread(task);
        thread.start();
    }
}

3、Thread类API

方法描述
public static Thread currentThread()返回当前的线程
public final String getName()返回线程名称
public final void setPriority(int priority)设置线程优先级
public void start()开始执行线程
public static void sleep(long m)使目前的线程休眠m毫秒
public final void yield()暂停目前的线程,运行其他 线程
public void run()线程要执行的任务
  1. setPriority :调整线程优先级,Java线程有优先,优先级高的线程会获得较多的运行机会。

  Java线程的优先级用整数表示,取值范围是1~10,Thread类有以下三个静态常量:

static int MAX_PRIORITY
          线程可以具有的最高优先级,取值为10。
static int MIN_PRIORITY
          线程可以具有的最低优先级,取值为1。
static int NORM_PRIORITY
          分配给线程的默认优先级,取值为5。

  1. sleep():线程休眠,会让当前线程处于阻塞状态,指定时间过后,线程就绪状态

  2. yield():暂停当前正在执行的线程对象,并执行其他线程

    yield()方法不是阻塞方法。让当前线程让位,让给其它线程使用。

    yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”

    注意:在回到就绪之后,有可能还会再次抢到。

  3. interrupt:中断线程,仅仅发送了一个中断的信号,当碰到wait(),sleep方法时,清除中断标记,抛出异常。

  4. setDaemon:设置线程为后台(守护)线程。

sleep()和yield()的区别

sleep():线程休眠,时间到后会自动唤醒变成就绪状态 yield(): 线程礼让,暂停当前线程的执行,让出时间片,变成就绪状态,再和其他线程抢时间片

4、线程状态

  1. 新建:new Thread()
  2. 就绪:start()
  3. 运行:抢到时间片后
  4. 阻塞:调用sleep(),wait(),join()方法
  5. 死亡:正常结束或者异常退出

5、多线程数据安全问题

数据安全问题

  • 条件1:多线程并发
  • 条件2:有共享数据
  • 条件3:共享数据有修改的行为

示例:模拟多个线程实现取钱操作

  1. 创建Account类
  2. 创建自定义线程类实现取款操作
  3. 创建多个线程实现取款,观察数据的变化

Account类:

package com.bai.work.account;

public class Account {
    private String no;
    private int doller;

    public Account() {
    }

    public Account(String no, int doller) {
        this.no = no;
        this.doller = doller;
    }

    public String getNo() {
        return no;
    }

    public void setNo(String no) {
        this.no = no;
    }

    public int getDoller() {
        return doller;
    }

    public void setDoller(int doller) {
        this.doller = doller;
    }

    public void getmoney(int money){
        int before=this.doller;
        int after=before-money;
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        this.doller=after;
        System.out.println(Thread.currentThread().getName()+"取了"+money+"-----余额:"+this.doller);
    }
}

  AccountThread类

package com.bai.work.account;

public class AccountThread extends Thread{
    Account account;

    public AccountThread(Account account) {
        this.account = account;
    }

    @Override
    public void run() {
        account.getmoney(500);
    }
}

 Test测试类

package com.bai.work.account;

public class AccountTest {
    public static void main(String[] args) {
        Account account=new Account("66666",2000);
        AccountThread a1=new AccountThread(account);
        AccountThread a2=new AccountThread(account);
        AccountThread a3=new AccountThread(account);
        a1.start();
        a2.start();
        a3.start();
    }
}

6、线程同步

线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作

线程同步的利弊

  • 好处:解决了线程同步的数据安全问题
  • 弊端:当线程很多的时候,每个线程都会去判断同步上面的这个锁,很耗费资源,降低效率

实现同步的方式:

  • 基于synchornized(同步)实现

            同步代码块

synchronized(this){
方法体
} 

        普通同步方法:

        修饰符 synchronized 返回值类型 方法名(形参列表){

        方法体

        }

         静态同步方法:

修饰符 synchronized static 返回值类型 方法名(形参列表){

方法体

}

  • 基于Lock实现

public void getMoney(double m) {
        lock.lock();
        double befor = this.money;
        double after = befor - m;
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            System.out.println(e.getMessage());
        }
        this.money = after;
        System.out.println(Thread.currentThread().getName() + "取钱:" + m + "----余额:" + this.money);
        lock.unlock();
    }
  • synchronized与Lock的对比

  1. 相同点:用来实现同步

  2. 不同点:

  • synchronized:关键字,同步代码块,同步实例方法,同步静态方法,自动解锁,jdk1.6在之后对synchronized进行了优化,性能得到了提升。
  • Lock:接口,提供的有各种实现类,手动的加锁和解锁,使用try和finally,性能更好,灵活性更高

7、死锁

死锁:当多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,从而导致两个或者多个线程都在等待对方释放资源,都停止执行的情况。

package com.bai.java2.test;

public class Test8 {
    public static void main(String[] args) {
        Object lipstick = new Object();//口红
        Object mirror = new Object();//镜子
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lipstick) {
                    System.out.println(Thread.currentThread().getName() + "拥有口红");
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        System.out.println(e.getMessage());
                    }

                    synchronized (mirror) {
                        System.out.println(Thread.currentThread().getName() + "还想拥有镜子");
                        try {
                            Thread.sleep(200);
                        } catch (InterruptedException e) {
                            System.out.println(e.getMessage());
                        }
                    }
                }
            }
        }).start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (mirror) {
                    System.out.println(Thread.currentThread().getName() + "拥有镜子");
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        System.out.println(e.getMessage());
                    }

                    synchronized (lipstick) {
                        System.out.println(Thread.currentThread().getName() + "还想拥有口红");
                        try {
                            Thread.sleep(200);
                        } catch (InterruptedException e) {
                            System.out.println(e.getMessage());
                        }
                    }
                }
            }
        }).start();
    }
}

8、线程通信

方法描述
public final void wait()线程等待(阻塞) ,释放锁 ,用在同步方法中的。
public final void wait(long timeout)线程等待,并指定等待时间,以 毫秒为单位
public void notify()唤醒一个等待的线程
public void notifyAll()唤醒全部等待的线程

wait方法和notify方法不是通过线程对象调用,而是在加锁的对象上进行调用

Object o = new Object();
o.wait();   //让正在o对象上活动的线程进入等待状态,无期限等待,直到被唤醒为止
o.notify();   //唤醒正在o对象上等待的线程。
o.notifyAll();   //这个方法是唤醒o对象上处于等待的所有线程

public static void main(String[] args) {
//        制水机:生成水
//        某同学:喝水
        final Object obj = new Object();//锁对象
        new Thread(new Runnable() {//制水机:生成水
            @Override
            public void run() {
                synchronized (obj) {
                    System.out.println("制水机正在生成水,请稍后...");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println("储水罐已满");
                    try {
                        obj.wait();// 在该锁上活动的线程处于等待状态,释放锁
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println("制水线程已经可以继续制水了");
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (obj) {
                    System.out.println("某某同学正在接水...");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println("储水罐的水已经空了");
                    obj.notify();//唤醒在该对象上等待的线程
                    System.out.println("唤醒等待的线程");
                }
            }
        }).start();

    }

9、线程池

线程池就是首先创建一些线程,他们的集合称之为线程池。

线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后再需要执行新的任务时重用这些线程而不是新建线程。

创建线程池

public class Test1 {
    public static void main(String[] args) {
        /*
         * 1:核心线程数
         * 2:最大线程数:和核心线程数一致
         * 3:闲置线程的存活时间:数字
         * 4:时间单位
         * 5:任务队列:阻塞队列
         * 6、线程工厂:创建新线程的方式,使用默认工厂
         * 7、拒绝策略:默认,抛出异常
         */
        ThreadPoolExecutor pool=new ThreadPoolExecutor(
                5,//核心线程数
                5,            //最大线程数:和核心线程数一致
                60,           //闲置线程的存活时间
                TimeUnit.SECONDS,//时间单位
                new ArrayBlockingQueue<>(20));//任务队列:阻塞队列

线程池的工作流程

  • 核心线程数:5
  • 存放要执行任务的等待队列:
  • 最大线程数:20
  • 拒绝策略:
  1. 提交任务,判断核心线程是否已满,如果未满,创建核心线程用来处理任务;如果已满进入下一步
  2. 判断阻塞队列是否已满,如果未满,将任务放入队列中,等待执行,如果已满,进入下一步
  3. 如果线程池中的最大线程未满,创建线程执行任务,如果已满,按照拒绝策略进行处理。

关闭线程方法:shutdown和shutdownNow的区别

  • shutdown():仅仅是不再接受新的任务,以前的任务还会继续执行
  • shutdownNow():立刻关闭线程池,如果线程池中还有缓存的任务没有执行,则取消执行,并返回这些任务
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值