多线程全面详解

线程

从软硬件实现多条执行流程的技术

多线程的创建

  1. 继承Thread类

优点:编码简单

缺点:无法继承其他类了,不利于扩展

public class Thread_1 {
    //3.创建线程对象
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();//直接调用run方法会被当成普通方法执行,调用start方法才是启动一个新的的线程
        //主线程,要把子线程任务放到主线程之前,否则就是单线程了
        for (int i = 0; i < 3; i++) {
            System.out.println("主线程"+i);
        }
    }
}
//1.继承Thread类
class MyThread extends Thread {
    //2.重写run方法
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println("子线程"+i);
        }
    }
}
  1. 实现Runnable接口

优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强

缺点:编程多一层对象包装,如果线程有执行结果不可以直接返回

public class Thread_2 {
    public static void main(String[] args) {
        // 2.创建MyRunnable对象
        Runnable runnable = new MyRunnable();
        // 3.把MyRunnable对象交给Thread处理
        Thread thread = new Thread(runnable);
        // 4.调用线程对象的start方法启动线程
        thread.start();

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

//简化版
public class Thread_2 {
    public static void main(String[] args) {

         new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 3; i++) {
                    System.out.println("子线程"+i);
                }
            }
        }).start();
         
        //主线程
            for (int i = 0; i < 3; i++) {
                System.out.println("主线程"+i);
            }
    }
}
  1. 实现Callable接口

优点:可以得到线程执行的结果

缺点:代码相对较多

public class Thread_3 {
    public static void main(String[] args) {

        Callable call = new MyCallable(100);
        //把Callable对象交给Futuretask对象,可以交给thread处理,并获得结果
        FutureTask<String> task = new FutureTask<String>(call);
        new Thread(task).start();

        //主线程
        Callable call2 = new MyCallable(200);
        FutureTask<String> task2 = new FutureTask<String>(call2);
        new Thread(task2).start();

        try {
            //通过get方法获取数据
            String s = task.get();
            System.out.println("第一个"+s);
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            String s1 = task2.get();
            System.out.println("第二个"+s1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class MyCallable implements Callable<String>{
    private int n;
    public MyCallable(int n) {
        this.n = n;
    }
    @Override
    public String call() throws Exception {
        n+=1;
        return "结果为"+n;
    }
}

线程常用方法

setName(),getName(),currentThread()

public class ThreadDemo01 {
    // main方法是由主线程负责调度的
    public static void main(String[] args) {
        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("最牛的线程");

        for (int i = 0; i < 5; i++) {
            System.out.println( m.getName() + "输出:" + i);
        }
    }
}
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);
        }
    }
}

线程的休眠

public class ThreadDemo02 {
    // main方法是由主线程负责调度的
    public static void main(String[] args) throws Exception {
        for (int i = 1; i <= 5; i++) {
            System.out.println("输出:" + i);
            if(i == 3){
                // 让当前线程进入休眠状态
                // 段子:项目经理让我加上这行代码,如果用户愿意交钱,我就注释掉。
                Thread.sleep(3000);
            }
        }
    }
}

线程同步

为了解决线程安全问题,让多个线程实现先后依次访问共享资源

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

方式一:同步代码块

把出现线程安全问题的核心代码上锁,用synchronized环绕,不推荐,一把锁住了所有线程

会影响其他无关线程的执行

规范:1.建议使用共享资源作为锁对象

2.实例方法建议使用this作为锁对象

3.静态方法用字节码(类名.class)作为锁对象

synchronized (this) {
            // 2、判断余额是否足够
            if (this.money >= money) {
                // 钱够了
                System.out.println(name + "来取钱,吐出:" + money);
                // 更新余额
                this.money -= money;
                System.out.println(name + "取钱后,余额剩余:" + this.money);
            } else {
                // 3、余额不足
                System.out.println(name + "来取钱,余额不足!");
            }

方式二:同步方法

给核心方法上锁,每次只能一个线程进入方法,执行后解锁,其它线程再次进入

public synchronized void drawMoney(double money) 

方式三:同步锁

private final Lock lock = new ReentrantLock();
public void drawMoney(double money) {
        // 1、拿到是谁来取钱
        String name = Thread.currentThread().getName();
        // 2、判断余额是否足够
        // 小明  小红
        lock.lock(); // 上锁
        try {
            if(this.money >= money){
                // 钱够了
                System.out.println(name+"来取钱,吐出:" + money);
                // 更新余额
                this.money -= money;
                System.out.println(name+"取钱后,余额剩余:" + this.money);
            }else{
                // 3、余额不足
                System.out.println(name+"来取钱,余额不足!");
            }
        } finally {
            lock.unlock(); // 解锁
        }
    }

线程池

可以复用线程的技术,反复创建线程的开销会很大,会严重影响系统性能

问题:

  • 谁代表线程池?

ExecutorService接口

实现类:

  • 临时线程什么时候创建?

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

  • 什么时候会开始拒绝任务?

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

线程池处理Runnable任务

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);

线程池处理Callable任务

 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));
        System.out.println(f1.get());

Executors得到线程池对象的常用方法

定时器

方式一:Timer定时器

Timer timer = new Timer();  // 定时器本身就是一个单线程。
        // 2、调用方法,处理定时任务
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "执行AAA~~~" + new Date());
            }
        }, 0, 2000);

方式二:ScheduledExecutorServiced定时器

 // 1、创建ScheduledExecutorService线程池,做定时器
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(3);

        // 2、开启定时任务
        pool.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "执行输出:AAA  ==》 " + new Date());

并发:CPU分时轮询的执行线程

并行:同一时刻同时在执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值