前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >多线程基础

多线程基础

原创
作者头像
hhss
修改2021-02-14 15:26:51
3080
修改2021-02-14 15:26:51
举报

一、线程简介

进程:CPU分配资源的单位,拥有资源的独立单位;一个进程至少有一个线程;

线程:CPU调度和执行的单位,只拥有一点运行中必不可少的资源(程序计数器、一组寄存器和栈);线程会带来额外的开销,比如CPU调度时间,并发控制(多个线程操作同一份资源)。

默认线程有哪些?

main线程:用户线程

gc线程:守护线程

二、线程实现

1、继承Thread类

不建议使用,因为Java单继承的局限性。

2、实现Runnable接口

建议使用,可以避免单继承的局限性;

方便同一个对象资源被多个线程同时使用,但可能出现线程同步问题,需要并发控制。

3、实现Callable接口

首先创建目标对象

然后创建执行服务

三、线程状态

线程的api

sleep:使线程进入阻塞状态,可以用来模拟延时或倒计时;sleep时候是不释放锁的,抱着锁睡觉。

join:使该线程强行加入运行状态,其他线程进入阻塞状态;待此线程执行完成之后,再执行其他线程。

yeild:线程让步,从运行状态变成就绪状态;让CPU重新调度,使用礼让不一定成功,看CPU的调度。

setPriority:优先级越大,执行的可能性越大;最大优先级是10;

四、线程同步

多线程操作同一份资源,会出现并发安全问题。需要队列+锁才能解决并发安全的问题。

三个不安全案例:

1、购买火车票

synchronized锁住buy()方法可解决安全问题。

2、多人同时取钱问题

synchronized锁住account对象,将run里面的逻辑放入代码块里即可。

3、线程不安全的集合

在循环中synchronized锁住list,再把逻辑放入代码块中即可。

CopyOnWriteArrayList代替List也可以解决同步安全问题,不需加synchronized。这个是JUC包的工具类,学习JUC时再详细看看。

4、synchronized解决同步安全问题:

可以锁方法(对象本身或类)和代码块(锁的是共享资源,变化的量,需要增删改的对象)。

5、使用synchronized可能引发死锁问题

同一个代码块拥有两个以上对象的锁,就看发生死锁问题。

产生死锁的四个必要条件:

  1. 互斥条件:一个资源每次只能被一个进程使用
  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
  3. 不剥夺条件:进程已获得的资源,在未使用完前,不能强行剥夺
  4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系

https://blog.csdn.net/wljliujuan/article/details/79614019

五、线程通信(线程协作)

线程间的通信目的主要用于线程同步,使用线程没有像进程通信中的用于数据交换的通信机制。

由生产者消费者问题引出线程通信的必要性。

解决方式一:管程法(增加缓冲区)

代码语言:javascript
复制
//思路

//1.思考需要哪些对象?
// 生产 , 消费 , 产品 , 容器

//2.分工

/*
    生产者只管生产
    消费者只管消费
    鸡: 实体类

    容器 :

    容器添加数据.
    要判断容器是否满 , 满了等待消费者消费
    没有满,通知生产者生产

    容器减少数据
    判断还有没有数据, 没有数据的话 . 等待生产者生产
    消费完毕 , 通知生产者生产
 */


import java.sql.SQLOutput;

//测试生产者和消费者问题
public class TestPC {
    public static void main(String[] args) {
        SynContainer synContainer = new SynContainer();

        new Productor(synContainer).start();
        new Consumer(synContainer).start();
    }
}


//生产者
class Productor extends Thread{
    //需要向容器中加入产品
    SynContainer container;
    public Productor(SynContainer container){
        this.container = container;
        }
        @Override
        public void run() {
            for (int i = 1; i < 100; i++) {
                //生产者添加产品
            container.push(new Chicken(i));
            System.out.println("生产者生产了"+i+"鸡");
        }
    }
}

//消费者
class Consumer extends Thread{
    SynContainer container;
    public Consumer(SynContainer container){
        this.container = container;
    }
    @Override
    public void run() {
        for (int i = 1; i < 100; i++) {
            //消费者拿走产品
            Chicken chicken = container.pop();
            System.out.println("消费者消费了"+chicken.id+"鸡");
        }
    }
}


//缓冲区-->容器
class SynContainer{

    //容器
    Chicken[] chickens = new Chicken[10];



    //容器的计数器
    int num = 0;

    //生产者放入产品
    public synchronized void push(Chicken chicken) {

        //假如容易已经满了,就不用放,等待消费者消费
        if (num>=chickens.length){

            //等待消费者消费
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        //假如容器没有满 , 通知生产生成

        System.out.println("num,,,,,"+num);
        chickens[num] = chicken;
        System.out.println("数组有多少个元素"+num);
        num++;
        //通知消费者消费
        this.notifyAll();

    }

    //消费者拿走产品
    public synchronized Chicken pop(){
        //假如容器空的,等待
        if (num<=0){
            //等待生产
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        num--;
        Chicken chicken = chickens[num];
        //通知生产者生产
        this.notifyAll();
        return chicken;
    }


}

//产品->鸡
class Chicken {
    int id;

    public Chicken(int id) {
        this.id = id;
    }
}

解决方式二:信号灯法(增加标志位)

代码语言:javascript
复制
//生产者消费2
//生产者--->演员
//消费者--->观众
//产品:信号灯--->电视----->声音

public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();

        new Player(tv).start();
        new Watcher(tv).start();
    }
}


//生产者
class Player extends Thread{
    TV tv;

    public Player(TV tv){
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i%2==0){
                this.tv.play("节目:快乐大本营播放中");
                System.out.println();
            }else {
                this.tv.play("广告:抖音,记录美好生活");
            }
        }
    }
}

//消费者
class Watcher extends Thread{
    TV tv;
    public Watcher(TV tv){
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}

//电视
class TV{
    //演员说话 , 观众等待
    //观众观看 , 演员等待
    boolean flag = true;

    //说话
    String voice;

    //表演
    public synchronized void play(String voice){

        //演员等待
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("表演了"+voice);
        this.voice = voice;

        //让观众观看
        this.notifyAll();
        this.flag = !this.flag;

    }


    //观看
    public synchronized void watch(){


        //观众等待
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("观众听到了: "+voice);

        //通知演员说话
        this.notifyAll();

        this.flag = !this.flag;
    }

}

六、高级主题

线程池的使用

  • 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影 响很大。
  • 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
  • 好处:
  1. 提高响应速度(减少了创建新线程的时间)
  2. 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
  3. 便于线程管理(…)
  • corePoolSize:核心池的大小
  • maximumPoolSize:最大线程数
  • keepAliveTime:线程没有任务时最多保持多长时间后会终止
  • JDK 5.0起提供了线程池相关API:ExecutorService 和 Executors
  1. ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable Future submit(Callable task):执行任务,有返回值,一般用来执行Callable void shutdown() :关闭连接池
  2. Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池

案例一:

代码语言:javascript
复制
//线程创建方式
/*
1.继承Thread
2.实现Runnable接口
3.实现Callable接口
4.使用线程池
 */


import java.util.concurrent.*;

class MyThread1 extends Thread{
    @Override
    public void run() {
        System.out.println("MyThread1");
    }
}


class MyThread2 implements Runnable{
    @Override
    public void run() {
        System.out.println("MyThread2");
    }
}

class MyThread3 implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        System.out.println("MyThread3");
        return 200;
    }
}


public class ThreadNew {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        new MyThread1().start();
        new Thread(new MyThread2()).start();


        FutureTask<Integer> future = new FutureTask<Integer>(new MyThread3());
        new Thread(future).start();


        Integer integer = future.get();
        System.out.println(integer);

    }


}

结果输出: MyThread1 MyThread2 MyThread3 200

案例二:

代码语言:javascript
复制
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

//使用线程池
public class ThreadPool{

    public static void main(String[] args) {

        //创建一个线程池(池子大小)
        ExecutorService pool = Executors.newFixedThreadPool(10);

        //执行runnable接口实现类
        pool.execute(new MyThread4());
        pool.execute(new MyThread4());
        pool.execute(new MyThread4());
        pool.execute(new MyThread4());

        //关闭连接池
        pool.shutdown();

    }

}

class MyThread4 implements Runnable{
    @Override
    public void run() {
        System.out.println("MyThread4");
    }
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、线程简介
  • 二、线程实现
    • 1、继承Thread类
      • 2、实现Runnable接口
        • 3、实现Callable接口
        • 三、线程状态
          • 线程的api
          • 四、线程同步
            • 1、购买火车票
              • 2、多人同时取钱问题
                • 3、线程不安全的集合
                  • 4、synchronized解决同步安全问题:
                    • 5、使用synchronized可能引发死锁问题
                    • 五、线程通信(线程协作)
                      • 由生产者消费者问题引出线程通信的必要性。
                        • 解决方式一:管程法(增加缓冲区)
                          • 解决方式二:信号灯法(增加标志位)
                          • 六、高级主题
                            • 线程池的使用
                              • 案例一:
                                • 案例二:
                                相关产品与服务
                                容器服务
                                腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                                领券
                                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档