线程的学习

1.概念

  • 线程(thread)是一个程序内部的一条执行路径
  • main方法的执行其实就是一个单独的执行路径
  • 程序中如果只有一条执行路径,那么这个程序就是单线程的程序

2.多线程是什么

多线程是指程序中有多条执行流程的技术

例如:消息通信,淘宝,京东系统都离不开多线程技术

1.继承Thread类

  1. 定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法.
  2. 创建MyThread类的对象.
  3. 调用线程对象的start()方法启动线程(启动后还是执行run()方法的).
  • 注意:直接调用run()方法会被当做普通方法执行,此时相当于单线程执行
  • 只有调用start方法才是启动一个新的线程执行
  • 把主线程任务放在子线程之前会出现什么问题?这样主线程一直会先跑完的,相当于是一个单线程的效果了
 public static void main(String[] args) {
        //main方法本身是一个单线程在执行的,称为主线程
        //3.创建线程对象
        Thread t=new Mythread();
        //4.调用线程对象的start方法启动线程(最终还是调用线程的run方法)
        t.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程输出"+i);
        }
    }
}
/**1.定义线程类继承了Thread类
 * */
class Mythread extends Thread{
    //2.重写了run方法
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("子线程输出"+i);
        }
    }
}

2.实现Runnable接口

  • ①定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法
  • ②创建MyRunnable任务对象
  • ③把MyRunnable任务对象交给Thread处理
  • ④调用线程对象的start()方法启动线程 
 public static void main(String[] args) {
        //3.创建一个线程任务类的对象(此对象不是线程对象)
        Runnable target=new MyRunnable();
        //4.把线程任务对象交给Thread线程对象
        Thread t=new Thread(target);
        //5.启动多线程
        t.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("main线程任务输出"+i);
        }

    }
}
/**1.定义一个线程任务类实现Runnable接口
 * */
class MyRunnable implements Runnable{
    //2.重写run()方法
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("子线程任务输出"+i);
        }
    }
}

3.实现Runnable接口(使用匿名内部类) 

还可以用Lamdba表达式简化

Runnable target1=new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("子线程任务2输出" + i);
                }
            }
        };
        Thread t1=new Thread(target1);
        t1.start();

4. 实现Callable,FutureTask接口实现

  1. 得到任务对象:①定义类实现Callable接口,实现call方法,封装要做的事情②用FutureTask和Callable对象封装成线程任务对象
  2. 把线程任务对象交给Thread处理
  3. 调用Thread的start方法启动线程,执行任务
  4. 线程执行完毕后,通过FutureTask的get方法去获取任务执行的结果
  public static void main(String[] args) {
        //3.创建一个Callable的执行对象
        Callable<String> call1=new MyCallable(100);
        //4.把Callable包装成未来任务对象
        FutureTask<String> f1=new FutureTask<>(call1);
        //5.交给Thread对象
        Thread t1=new Thread(f1);
        //6.启动线程
        t1.start();

        Callable<String> call2=new MyCallable(999);
        FutureTask<String> f2=new FutureTask<>(call2);
        Thread t2=new Thread(f2);
        t2.start();

        //7.得到线程执行完毕后的结果
        try {
            //主线程执行到这里,如果上面第一个线程没有执行完毕,这里会让出cpu,等待第一个线程执行完毕之后才能执行这个代码取结果
            String s1=f1.get();
            System.out.println(s1);
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            String s2=f2.get();
            System.out.println(s2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
/**
 * 1.定义一个类实现callable接口
 */
class MyCallable implements Callable<String>{
    private int n;
    public MyCallable(int n){
        this.n=n;
    }
    //2.重写call方法,定义执行的任务和返回的结果
    @Override
    public String call() throws Exception {
        //需求:计算0-100的和
        int sum=0;
        for (int i = 0; i < n; i++) {
            sum+=i;
        }
        return "子线程帮助计算0-"+n+"的和是:"+sum;
    }
}

3.Thread常用方法

4.线程安全问题

多个线程同时操作同一个共享资源的时候可能会出现业务安全问题,称为线程安全问题 

1)线程安全问题出现的原因?

  • 存在多线程并发
  • 同时访问共享资源
  • 存在修改共享资源 

2)线程安全问题的解决方案

  • 解决方案1:同步代码块
 synchronized (this) {
出现问题的代码
}
  • 解决方案2:同步方法:出现问题的方法用synchronized修饰
public synchronized void drawMoney(double money){
}
  • 解决方案3:lock锁
 //锁对象属于账户对象,而且唯一不可更改
    private final ReentrantLock lock=new ReentrantLock();
lock.lock();//上锁
try{
}
 finally {
            lock.unlock();//解锁

        }

5.线程通信

1)概念:线程间相互发送数据.

2)常见形式

  • 通过共享一个数据的方式实现
  • 根据共享数据的情况决定自己该怎么做,以及通知其他线程怎么做

3)线程通信的三个方法

  • wait()
  • notify()
  • notifyAll()

 注意:上述方法应该使用当前同步锁对象进行调用

6.线程池(重点)

概念:可以复用的技术;

代表:JDK5.0起提供了代表线程的接口:ExecutorService

如何得到线程池对象?

  • 方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象
  • 方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象

1)面试高频题目:

 临时线程什么时候创建?新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程.

什么时候会开始拒绝任务?核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始任务拒绝.

2)Executor方法

3)新任务的拒绝策略 

4)线程池操作Runnable任务 

  public static void main(String[] args) {
        /**  public ThreadPoolExecutor(int corePoolSize,
         int maximumPoolSize,
         long keepAliveTime,
         TimeUnit unit,
         BlockingQueue<Runnable> workQueue,
         ThreadFactory threadFactory,
         RejectedExecutionHandler handler) {
         * */
        Executor pool=new ThreadPoolExecutor(3,5,10, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(5),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        //提交任务给线程处理
        Runnable my=new MyRun();
        pool.execute(my);
        pool.execute(my);
        pool.execute(my);
        pool.execute(my);
        pool.execute(my);
        pool.execute(my);//排满了,临时线程的时机
        pool.execute(my);
        pool.execute(my); //出现任务拒绝的时机
        pool.execute(my);
    }
}
/**1.定义一个线程任务类实现Runnable接口
 * */
class MyRun implements Runnable{
    //2.重写run()方法
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"输出"+i);
        }
        try {
            Thread.sleep(100000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 5)线程池操作callable任务

public class d6_线程池实现callable {
    public static void main(String[] args) {
        ExecutorService pool1=new ThreadPoolExecutor(3,5
                ,10, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(5),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        Future<String> s1 = pool1.submit(new MyCall(100));
        try {
            System.out.println(s1.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
        Future<String> s2 = pool1.submit(new MyCall(200));
        try {
            System.out.println(s2.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
        Future<String> s3 = pool1.submit(new MyCall(300));
        try {
            System.out.println(s3.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
        Future<String> s4 = pool1.submit(new MyCall(400));
        try {
            System.out.println(s4.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
/**
 * 1.定义一个类实现callable接口
 */
class MyCall implements Callable<String> {
    private int n;
    public MyCall(int n){
        this.n=n;
    }
    //2.重写call方法,定义执行的任务和返回的结果
    @Override
    public String call() throws Exception {
        //需求:计算0-100的和
        int sum=0;
        for (int i = 0; i < n; i++) {
            sum+=i;
        }
        return  Thread.currentThread().getName()+"计算1-"+n+"的和是:"+sum;
    }
}

6)Executors使用可能存在的陷阱

public class d7_线程池 {
   public static void main(String[] args) {
      ExecutorService pool=Executors.newFixedThreadPool(3);
      pool.submit(new MyRun1());
      pool.submit(new MyRun1());
      pool.submit(new MyRun1());
      pool.submit(new MyRun1());
      pool.submit(new MyRun1());
      pool.submit(new MyRun1());
   }
}
/**1.定义一个线程任务类实现Runnable接口
 * */
class MyRun1 implements Runnable{
   //2.重写run()方法
   @Override
   public void run() {
      for (int i = 0; i < 10; i++) {
         System.out.println(Thread.currentThread().getName()+"输出"+i);
      }
      try {
         Thread.sleep(100000);
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}

 7.定时器

  • 推荐使用ScheduleExecutorService
  • 基于线程池 某个任务执行出现问题的情况不会影响其他定时任务的执行
  ScheduledThreadPoolExecutor pool=new ScheduledThreadPoolExecutor(3);
        pool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"----aaaa");
            }
        },0,2, TimeUnit.SECONDS);
        pool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"----bbbbb");
            }
        },0,1, TimeUnit.SECONDS);

 8.并发和并行

概念:正在运行的程序(软件)被称为进程,线程是输入进程的,多线程其实是并发与并行同时存在的

并发理解:

  • CPU同时执行线程的数量:与CPU的核数和逻辑处理器有关系;
  • CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们感觉这些线程在同时执行,这就是并发

并行的理解:同一时刻同时在执行

9.线程的6种状态

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值