Java中线程的学习

一、线程的概述

1、线程:

a.线程(Thread)是一个程序内部的一条执行路径

b.在启动程序执行后,main方法的执行就是一条单独的执行路径

c.程序中如果只有一条执行路径,那麽这个程序就是单线程的程序

2、多线程

是指从软硬件上实现多条执行流程技术

二、线程创建的三种方式

1、线程创建方式一

注意事项:

实现代码:

package thread.app.d1_create;

/**
 * 目标:多线程创建方式一:继承Thread类实现
 */
public class ThreadDemo1 {
    public static void main(String[] args) {
        //3.new一个新线程对象
        Thread t = new MyThread();
        //4.调用start方法启动线程(执行的还是run)
        t.start();

        //主线程输出
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程输出:" + i);
        }

    }
}

/**
 * 定义一个线程类继承Thread类
 */
class MyThread extends Thread {
    /**
     * 2.重写run方法,里边定义线程要做什么
     */
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程输出:" + i);
        }
    }
}

 2、线程创建方式二

a.如何创建线程:

b.优缺点

 实现代码:

package thread.app.d1_create;

/**
    目标:学会线程的创建方式二,理解优缺点
 */
public class ThreadDemo2 {
    public static void main(String[] args) {
        //3.创建任务对象
        Runnable target=new MyRunnble();
        //4.把任务对象交给Thread处理
        Thread thread=new Thread(target);
        //5.启动线程
        thread.start();

        //主线程任务
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程执行输出:"+i);
        }
    }
}

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

 3.线程创建方式三

a.利用Callable,FutureTask接口实现

 

 b.FutureTask的API及其优缺点

 

 实现代码:

package thread.app.d1_create;

import java.util.Calendar;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.function.Function;

/**
 * 目标:学会线程的创建方式三,实现Callable接口,结合FutureTask实现
 */
public class ThreadDemo3 {
    public static void main(String[] args) {
        //3.创建Callable任务对象
        Callable<String> call=new MyCallable(100);

        //4.做一次封装,把Callable对象封装交给FutureTask对象
        //FutureTask对象作用1:是Runnable的对象(实现了Runnable接口),可以交给Thread
        //FutureTask对象作用2:在线程执行完毕后通过调用其get方法得到线程执行的结果
        FutureTask<String> f1=new FutureTask<>(call);
        //5.交给线程进行处理
        Thread t1=new Thread(f1);
        //6.启动线程
        t1.start();


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


        try {
            //如果f1没有执行完毕,这里的代码会等待,知道线程1跑完才提取结果
            String rs1=f1.get();
            System.out.println("第一个结果:"+rs1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }


        try {
            String rs2=f2.get();
            System.out.println("第二个结果:"+rs2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException 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 {
        int sum=0;
        for (int i = 1; i < n; i++) {
            sum+=i;
        }
        return "子线程执行的结果为:"+sum;
    }
}

三、Thread的常用方法

Thread常用方法:获取线程名称getName,设置名称setName,获取当前线程对象currentThread,线程睡眠sleep。

实现代码:

MyThread类

package thread.app.d2_api;

public class MyThread extends Thread{
    public MyThread() {
    }

    //定义子类构造器用来写名字
    public MyThread(String name) {
        //为当前对象设置名称,送给父类有参构造器初始化名称
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+"子线程输出:"+i);
        }
    }
}

 ThreadDemo1类(实现类)

package thread.app.d2_api;

/**
    目标:线程API
 */
public class ThreadDemo1 {
    public static void main(String[] args) {
        //对线程t1与t2进行命名
        Thread t1=new MyThread("1号");
//        t1.setName("1号");
        t1.start();
        System.out.println(t1.getName());

        Thread t2=new MyThread("2号");
//        t2.setName("2号");
        t2.start();
        System.out.println(t2.getName());


        //哪个线程执行就得到那个线程对象(主线程对象)
        //主线程名称就叫main
        Thread m=Thread.currentThread();
        System.out.println(m.getName());
        //对主线程取名字
        m.setName("666");


        for (int i = 0; i < 5; i++) {
            System.out.println(m.getName()+"主线程输出:"+i);
        }
    }
}

  ThreadDemo2类(实现类)

package thread.app.d2_api;

/**
    目标:Thread类线程休眠方法的API(让当前线程休眠指定时间后再执行)
 */
public class ThreadDemo2 {
    //main方法是由主线程调度的
    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 5; i++) {
            System.out.println("输出:"+i);
            if (i==3){
                //让线程进入休眠状态
                Thread.sleep(3000);
            }
        }
    }
}

四、线程安全

1.线程安全问题

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

2.案例

模拟取钱案例 :小明,小红他们有一个共同账户,余额10万,模拟两人同时取10万。

实现代码:

Account类(账户类)

package thread.app.d3_thread_safe;

/**
    账户类
 */
public class Account {
    private String cardID;
    private double money;

    public Account() {
    }

    public Account(String cardID, double money) {
        this.cardID = cardID;
        this.money = money;
    }

    /**
        取钱方法
     * @param money
     */
    public void drawMoney(double money) {
        //0.先获取谁来取钱
        String name=Thread.currentThread().getName();
        //1.判断账户是否够钱
        if (this.money>=money) {
            //2.取钱
            System.out.println(name+"来取钱,吐出"+money);
            //3.更新余额
            this.money-=money;
            System.out.println("剩余的钱:"+this.money);

        }else {
            //4.余额不足
            System.out.println("余额不足!!!");
        }
    }

    public String getCardID() {
        return cardID;
    }

    public void setCardID(String cardID) {
        this.cardID = cardID;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }


}

DrawTread类 (取钱的线程类)

package thread.app.d3_thread_safe;

/**
 * 取钱的线程类
 */
public class DrawThread extends Thread {
    //同步接账户类对象,接收处理账户对象
    private Account acc;

    public DrawThread( Account acc,String name) {
        super(name);
        this.acc = acc;
    }

    @Override
    public void run() {
        //两人同时取钱的
        acc.drawMoney(100000);

    }
}

 ThreadDemo类(实现类)

package thread.app.d3_thread_safe;

/**
    需求:模拟取钱案例
        小明,小红他们有一个共同账户,余额10万,模拟两人同时取10万。
 */
public class ThreadDemo {
    public static void main(String[] args) {
        //1.定义线程类,创建一个共享账户
        Account acc=new Account("123456789",100000);

        //2.定义2个线程对象,代表小明,小红同时进来了
        new DrawThread(acc,"小明").start();
        new DrawThread(acc,"小红").start();


    }
}

运行截图:

  

五、线程同步

1.线程同步思想

加锁,把共享资源进行上锁,每次只能一个线程进入访问完毕以后解锁,然后其他线程才能进来。

2.加锁的方式

a.同步代码块

注意:

将取钱的代码进行上锁

/**
    取钱方法
 * @param money
 */
public void drawMoney(double money) {
    //0.先获取谁来取钱
    String name=Thread.currentThread().getName();
    //同步代码块,声明锁对象(实现线程安全,依次进入)
    //小明  小红
    //this==acc 共享账户
    synchronized (this) {
        //1.判断账户是否够钱
        if (this.money>=money) {
            //2.取钱
            System.out.println(name+"来取钱,吐出"+money);
            //3.更新余额
            this.money-=money;
            System.out.println("剩余的钱:"+this.money);

        }else {
            //4.余额不足
            System.out.println(name+"余额不足!!!");
        }
    }
}

b.同步方法

注意:

/**
    取钱方法
    同步方法,先进来的方法执行完后,才能再执行其他的
 * @param money
 */
public synchronized void drawMoney(double money) {
    //0.先获取谁来取钱
    String name=Thread.currentThread().getName();
    //1.判断账户是否够钱
    lock.lock();//上锁
    try {
        if (this.money>=money) {
            //2.取钱
            System.out.println(name+"来取钱,吐出"+money);
            //3.更新余额
            this.money-=money;
            System.out.println("剩余的钱:"+this.money);

        }else {
            //4.余额不足
            System.out.println("余额不足!!!");
        }
    } finally {
        //如果上方代码出现bug,也会执行解锁
        lock.unlock();//执行完后解锁
    }
}

c.Lock锁

 客户类的代码

package thread.app.d5_thread_synchronizes_method;

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

/**
    账户类
 */
public class Account {
    private String cardID;
    private double money;
    //final修饰后:锁对象是唯一,不可替换的
    private final Lock lock=new ReentrantLock();

    public Account() {
    }

    public Account(String cardID, double money) {
        this.cardID = cardID;
        this.money = money;
    }

    /**
        取钱方法
        同步方法,先进来的方法执行完后,才能再执行其他的
     * @param money
     */
    public void drawMoney(double money) {
        //0.先获取谁来取钱
        String name=Thread.currentThread().getName();
        //1.判断账户是否够钱
        lock.lock();//上锁
        try {
            if (this.money>=money) {
                //2.取钱
                System.out.println(name+"来取钱,吐出"+money);
                //3.更新余额
                this.money-=money;
                System.out.println("剩余的钱:"+this.money);

            }else {
                //4.余额不足
                System.out.println("余额不足!!!");
            }
        } finally {
            //如果上方代码出现bug,也会执行解锁
            lock.unlock();//执行完后解锁
        }
    }

    public String getCardID() {
        return cardID;
    }

    public void setCardID(String cardID) {
        this.cardID = cardID;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }


}

六、线程通信

  1.什么是线程通信,如何实现?

2.线程通信常见模型

 

3..线程通信的三种方式

注意:

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

七、线程池

1.线程池概述

线程池就是可以复用线程的技术。

2.线程池实现的API,参数说明

a.如何获得线程池对象

b.ThreadPoolExecutor构造器的参数说明

public ThreadPoolExecutor(int corePoolSize,
                            int maximumPoolSize,
                            long keepAliveTime,
                            TimeUnit unit,
                            BlockingQueue<Runnable> workQueue,
                            ThreadFactory threadFactory,
                            RejectedExecutionHandler handler)

 注意:

c.线程池处理Runnable任务

 实现代码:

MyRunnable类

package thread.app.d8_threadpool;

/**
 * @author zhixu
 * @date 2022/5/24
 * @apiNote
 */
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+"输出了:HelloWord==>"+i);
        }
        try {
            System.out.println(Thread.currentThread().getName()+"本任务与线程绑定,线程进入休眠!!!");
            Thread.sleep(10000000);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

实现类 

package thread.app.d8_threadpool;

import java.util.concurrent.*;

/**
 * @author zhixu
 * @date 2022/5/24
 * @apiNote
 * 目标:自定义一个线程池对象,并测试其性能
 */
public class ThreadPoolDemo1 {
    public static void main(String[] args) {
        //1.创建线程池对象
        /**
         public ThreadPoolExecutor(int corePoolSize,
                                     int maximumPoolSize,
                                     long keepAliveTime,
                                     TimeUnit unit,
                                     BlockingQueue<Runnable> workQueue,
                                     ThreadFactory threadFactory,
                                     RejectedExecutionHandler handler)
         */
        ExecutorService pool=new ThreadPoolExecutor(3,5,6
                , TimeUnit.SECONDS,new ArrayBlockingQueue<>(5)
                , Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy()
        );
        //2.给任务线程池处理
        Runnable target=new MyRunnable();
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);

        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        //创建临时线程
        pool.execute(target);
        pool.execute(target);
//        //不创建,拒绝策略出发!!!
//        pool.execute(target);

        //关闭线程池(一般不使用)
//        pool.shutdownNow();//立即关闭,即使任务没有完成,丢失任务
        pool.shutdown();//等所有任务完成后,再关闭


    }
}

运行截图:

 d.线程池处理Callable任务

实现代码:

MyCallable类

package thread.app.d8_threadpool;

import java.util.concurrent.Callable;

/**
 * 1.定义一个任务类 实现Callable接口,泛型。应该申明线程任务执行完毕后的结果的数据类型
 */
public class MyCallable implements Callable<String> {
    private int n;

    public MyCallable(int n) {
        this.n = n;
    }

    /*
        2.重写call方法
    */
    @Override
    public String call() throws Exception {
        int sum=0;
        for (int i = 1; i < n; i++) {
            sum+=i;
        }
        return Thread.currentThread().getName()+"执行1-"+n+"的和结果是:"+sum;
    }
}

实现类

package thread.app.d8_threadpool;

import java.util.concurrent.*;

/**
 * @author zhixu
 * @date 2022/5/24
 * @apiNote
 * 目标:自定义一个线程池对象,并测试其性能
 */
public class ThreadPoolDemo2 {
    public static void main(String[] args) throws Exception {
        //1.创建线程池对象
        /**
         public ThreadPoolExecutor(int corePoolSize,
                                     int maximumPoolSize,
                                     long keepAliveTime,
                                     TimeUnit unit,
                                     BlockingQueue<Runnable> workQueue,
                                     ThreadFactory threadFactory,
                                     RejectedExecutionHandler handler)
         */
        ExecutorService pool=new ThreadPoolExecutor(3,5,6
                , TimeUnit.SECONDS,new ArrayBlockingQueue<>(5)
                , Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy()
        );
        //2.给任务线程池处理
       Future<String> f1=pool.submit(new MyCallable(100));
       Future<String> f2=pool.submit(new MyCallable(400));
       Future<String> f3=pool.submit(new MyCallable(500));
       Future<String> f4=pool.submit(new MyCallable(600));

        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());



    }
}

 3.Executors工具类构建线程池对象

 注意:

Executors的底层也是基于线程池ThreadPoolExecutor创建线程池对象。

实现代码:

package thread.app.d8_threadpool;

import java.util.concurrent.*;

/**
 * @author zhixu
 * @date 2022/5/24
 * @apiNote
 * 目标:使用Executors的工具方法直接得到一个线程池对象(如果不注意,会出现系统风险)
 */
public class ThreadPoolDemo3 {
    public static void main(String[] args) throws Exception {
        //1.创建固定线程数量的线程池
        ExecutorService pool=Executors.newFixedThreadPool(3);//永远只有3个线程

        pool.execute(new MyRunnable());
        pool.execute(new MyRunnable());
        pool.execute(new MyRunnable());
        pool.execute(new MyRunnable());//已经没有多余的线程

        //2.



    }
}

2.Executors工具类使用可能存在的风险

 

八、定时器

1.定时器概念

定时器是一种控制任务延时调用,或周期调用的技术。

2.定时器的实现方式

a.Timer定时器(不推荐使用)

 注意:

 实现代码:

package thread.app.d9_timer;

import javax.xml.crypto.Data;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/**
 * @author zhixu
 * @date 2022/5/25
 * @apiNote
 * 目标:Timer定时器的使用和了解
 */
public class TimerDemo1 {
    public static void main(String[] args) {
        //1.创建Timer定时器
        Timer timer=new Timer();//定时器,本身就是一个单线程,不常用     (单线程,当上边的任务出现错误,会影响下个定时任务的执行)
        //2.调用方法,处理定时任务
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"执行A!!!"+new Date());
                //任务休眠,会干扰下个定时器的任务
//                try {
//                    Thread.sleep(50000);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
            }
        },0,2000);

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"执行B!!!"+new Date());
            }
        },0,2000);
    }
}

运行截图:

 b.ScheduledExecutorService定时器

 优点:

基于线程池,某个任务执行情况不影响其他定时任务的执行。

实现代码:

package thread.app.d9_timer;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author zhixu
 * @date 2022/5/25
 * @apiNote
 * 目标:Timer定时器的使用和了解(不常用)
 */
public class TimerDemo2 {
    public static void main(String[] args) {
        //1.创建ScheduledExecutorService线程池,做定时器(基于线程池,不影响其他定时任务的执行)
        ScheduledExecutorService pool= Executors.newScheduledThreadPool(3);

        //2.开始定时任务
        /**
         public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                     long initialDelay,
                                                     long period,
                                                     TimeUnit unit);
         */
        pool.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"输出执行:AAA"+new Date());
                //任务休眠,不会干扰下个定时器的任务
                try {
                    Thread.sleep(50000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },0,2, TimeUnit.SECONDS);

        pool.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"输出执行:BBB"+new Date());
            }
        },0,2, TimeUnit.SECONDS);
    }
}

 运行截图:

九、并发并行,线程的生命周期

1.并发与并行

并发与并行的含义:

 

 2.线程的生命周期

 

 

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值