多线程

多线程

概念

  • 并发:两个事件在同一时间段进行
  • 并行:两个事件在同一时刻进行
  • 程序:代码(指令和数据),静态
  • 进程Process:执行程序的一次过程,动态,系统分配资源的最小单位
  • 线程Thread:负责执行当前进程中程序的运行,一个进程中至少有一个线程,CPU调度和执行的单位
  • 多线程:真正的多线程是多个CPU多核的,模拟的多线程是单个CPU下进行的,只是时间短

创建线程

  • Thread class(不建议使用,避免单继承的局限性)

    线程同时进行,线程开启不一定立即执行,有CPU调度执行

    /*
        1.继承Thread
        2.重写run()方法
        3.创建线程对象,调用start()方法启动线程
     */
    public class ThreadDemo1 extends Thread{
        //重写run()方法
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println("线程" + i);
            }
        }
    
        public static void main(String[] args) {
            //创建线程对象
            ThreadDemo1 threadDemo1 = new ThreadDemo1();
            //调用start()方法启动线程
            threadDemo1.start();
    
    
            for (int i = 0; i < 20; i++) {
                System.out.println("main" + i);
            }
        }
    }
    
    //继承Thread  多线程下载网络资源图片
    public class ThreadDemo2 extends Thread{
    
        private String url;//下载地址
        private String name;//保存文件名
        public ThreadDemo2(String url,String name){
            this.url = url;
            this.name = name;
        }
        @Override
        public void run() {
            WebDownloader webDownloader = new WebDownloader();
            webDownloader.downloader(url,name);
            System.out.println("下载完成" + name);
        }
    
        public static void main(String[] args) {
            ThreadDemo2 t1 = new ThreadDemo2("\n" +
                    "https://tse2-mm.cn.bing.net/th/id/OIP-C.YGyq7JJFXnJQ-D0_fSTNYQHaE8?w=230&h=180&c=7&r=0&o=5&dpr=1.3&pid=1.7","图片1.jpg");
            ThreadDemo2 t2 = new ThreadDemo2("\n" +
                    "https://tse2-mm.cn.bing.net/th/id/OIP-C.jbBWziFsK6YJkSHTq_b3WQHaEK?w=273&h=180&c=7&r=0&o=5&dpr=1.3&pid=1.7","图片2.jpg");
            ThreadDemo2 t3 = new ThreadDemo2("\n" +
                    "https://tse3-mm.cn.bing.net/th/id/OIP-C.rF6jjFF44zwPwmeoMFx_MwHaLH?w=115&h=180&c=7&r=0&o=5&dpr=1.3&pid=1.7","图片3.jpg");
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    //下载器
    class WebDownloader{
        public void downloader(String url,String name){
            try {
                FileUtils.copyURLToFile(new URL(url),new File(name));
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("IO异常,downloader方法出现异常");
            }
        }
    }
    
  • Runable()接口(建议使用,灵活方便,可以一个对象被多个线程使用,避免了单继承的局限性)
    /*
    1.实现Runable接口
    2.重写run方法
    3.创建线程对象,通过线程对象start()开启线程
     */
    public class ThreadDemo3 implements Runnable{
    
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println("线程" + i);
            }
        }
    
        public static void main(String[] args) {
            //创建Runable接口实现类对象
            ThreadDemo3 threadDemo3 = new ThreadDemo3();
            //创建线程对象,通过线程对象调用start方法开启线程
    //        Thread thread = new Thread(threadDemo3);
    //
    //        thread.start();
            new Thread(threadDemo3).start();
    
            for (int i = 0; i < 1000; i++) {
                System.out.println("main" + i);
            }
        }
    }
    
    //使用Runable接口  多线程下载网络资源图片
    public class ThreadDemo4 implements Runnable{
    
        private String url;//下载地址
        private String name;//保存文件名
        public ThreadDemo4(String url,String name){
            this.url = url;
            this.name = name;
        }
    
        @Override
        public void run() {
            WebDownloader webDownloader = new WebDownloader();
            webDownloader.downloader(url,name);
            System.out.println("下载完成" + name);
        }
    
        public static void main(String[] args) {
            ThreadDemo4 t1 = new ThreadDemo4("\n" +
                    "https://tse2-mm.cn.bing.net/th/id/OIP-C.YGyq7JJFXnJQ-D0_fSTNYQHaE8?w=230&h=180&c=7&r=0&o=5&dpr=1.3&pid=1.7","图片1.jpg");
            ThreadDemo4 t2 = new ThreadDemo4("\n" +
                    "https://tse2-mm.cn.bing.net/th/id/OIP-C.jbBWziFsK6YJkSHTq_b3WQHaEK?w=273&h=180&c=7&r=0&o=5&dpr=1.3&pid=1.7","图片2.jpg");
            ThreadDemo4 t3 = new ThreadDemo4("\n" +
                    "https://tse3-mm.cn.bing.net/th/id/OIP-C.rF6jjFF44zwPwmeoMFx_MwHaLH?w=115&h=180&c=7&r=0&o=5&dpr=1.3&pid=1.7","图片3.jpg");
    
            new Thread(t1).start();
            new Thread(t2).start();
            new Thread(t3).start();
        }
    }
    
  • Callable()接口
    /*
    1.实现Callable接口
    2.重写call()方法,抛出异常
    3.创建目标对象
    4.创建执行服务
    5.提交执行对象
    6.获取结果
    7.关闭服务
     */
    public class ThreadDemo6 implements Callable<Boolean> {
        private String url;//下载地址
        private String name;//保存文件名
        public ThreadDemo6(String url,String name){
            this.url = url;
            this.name = name;
        }
    
        //重写call()方法,抛出异常
        @Override
        public Boolean call() {
            WebDownloader webDownloader = new WebDownloader();
            webDownloader.downloader(url,name);
            System.out.println("下载完成" + name);
            return true;
        }
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            //创建目标对象
            ThreadDemo6 t1 = new ThreadDemo6("\n" +
                    "https://tse2-mm.cn.bing.net/th/id/OIP-C.YGyq7JJFXnJQ-D0_fSTNYQHaE8?w=230&h=180&c=7&r=0&o=5&dpr=1.3&pid=1.7","图片1.jpg");
            ThreadDemo6 t2 = new ThreadDemo6("\n" +
                    "https://tse2-mm.cn.bing.net/th/id/OIP-C.jbBWziFsK6YJkSHTq_b3WQHaEK?w=273&h=180&c=7&r=0&o=5&dpr=1.3&pid=1.7","图片2.jpg");
            ThreadDemo6 t3 = new ThreadDemo6("\n" +
                    "https://tse3-mm.cn.bing.net/th/id/OIP-C.rF6jjFF44zwPwmeoMFx_MwHaLH?w=115&h=180&c=7&r=0&o=5&dpr=1.3&pid=1.7","图片3.jpg");
    
            //创建执行服务
            ExecutorService ser = Executors.newFixedThreadPool(3);
            //提交执行对象
            Future<Boolean> submit1 = ser.submit(t1);
            Future<Boolean> submit2 = ser.submit(t2);
            Future<Boolean> submit3 = ser.submit(t3);
            //获取结果
            Boolean r1 = submit1.get();
            Boolean r2 = submit2.get();
            Boolean r3 = submit3.get();
            //关闭服务
            ser.shutdownNow();
        }
    }
    

静态代理

类似于用Runable()接口实现多线程方式中通过Thread对象代理启动线程

/*
真实对象和代理对象实现同一个接口
代理对象可以做真实对象做不了的事,真实对象专注自己的事
*/

//模拟结婚婚庆公司代理操办婚礼
public class StaticProxy {
    public static void main(String[] args) {
        new WiddingServices(new You()).HappyMarry();
    }
}

//结婚
interface Marry{
    void HappyMarry();
}

//真实对象,结婚者
class You implements Marry{
    @Override
    public void HappyMarry() {
        System.out.println("结婚");
    }
}

//代理对象 婚庆公司
class WiddingServices implements Marry{

    private Marry target;//代理目标

    public WiddingServices(Marry target) {
        this.target = target;
    }

    @Override
    public void HappyMarry() {
        before();
        this.target.HappyMarry();
        after();
    }

    private void after() {
        System.out.println("婚前准备");
    }

    private void before() {
        System.out.println("收款");
    }
}

Lamda表达式

函数式接口:只有一个抽象方法的接口

对于函数式接口,用Lamda表达式创建对象

匿名内部类可以用Lamda表达式简写

//推导Lamda表达式
public class LamdaDemo1 {
    //2.静态内部类
    static class Like2 implements ILike {
        @Override
        public void lamda() {
            System.out.println("222");
        }
    }
    public static void main(String[] args) {
        Like1 like1 = new Like1();
        like1.lamda();

        Like2 like2 = new Like2();
        like2.lamda();

        //3.局部内部类
        class Like3 implements ILike {
            @Override
            public void lamda() {
                System.out.println("333");
            }
        }

        Like3 like3 = new Like3();
        like3.lamda();

        //4.匿名内部类
        ILike like4 = new ILike() {
            @Override
            public void lamda() {
                System.out.println("444");
            }
        };
        like4.lamda();

        //5.Lamda表达式
        ILike like5 = () -> {
            System.out.println("555");
        };
        like5.lamda();
    }
}
interface ILike{
    void lamda();
}
//1.实现类
class Like1 implements ILike{
    @Override
    public void lamda() {
        System.out.println("111");
    }
}

线程终止

弃用JDK提供的stop()和distory()方法,推荐使用标志位flag当做终止操作变量

当flag = false时,线程停止

public class ThreadStop implements Runnable{

    //1.设置一个停止标识
    private boolean flag = true;

    @Override
    public void run() {
        int i = 0;
        while (flag) {
            System.out.println("run....Thread" + i++);
        }
    }

    //2.设置公开方法更换标志位停止线程
    public void StopThread (){
        this.flag = false;
    }

    public static void main(String[] args) {
        ThreadStop stop = new ThreadStop();
        new Thread(stop).start();
        for (int i = 0; i < 1000; i++) {
            System.out.println(i);
            if (i == 800){
                stop.StopThread();
                System.out.println("线程停止了");
            }
        }
    }
}

线程休眠---------Thread.sleep()方法

  1. sleep()可以指定当前线程阻塞的毫秒数,时间达到后线程进入就绪状态
  2. 存在异常InterruptedException
  3. 每个对象都有一个锁,sleep不会释放锁
//打印当前时间
Date date = new Date(System.currentTimeMillis());//获取系统当前时间
        while (true){
            System.out.println(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(date));//转换时间格式并打印输出
            Thread.sleep(1000);
            date = new Date(System.currentTimeMillis());

线程礼让---------Thread.yield()方法

  1. 让当前线程进入就绪状态,供CPU调度
  2. 礼让不一定成功
public class ThreadYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "线程开始");

        Thread.yield();
        System.out.println(Thread.currentThread().getName() + "线程结束");
    }

    public static void main(String[] args) {
        ThreadYield threadYield = new ThreadYield();

        new Thread(threadYield,"A").start();
        new Thread(threadYield,"B").start();
    }
}

线程强制执行---------Thread.join()方法

jion()使主线程进入阻塞状态,待到加入的线程执行完毕

public class ThreadJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("VIP"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //启动线程
        ThreadJoin join = new ThreadJoin();
        Thread thread = new Thread(join);
        thread.start();
        //主线程
        for (int i = 0; i < 20; i++) {
            if (i == 10){
                thread.join();
            }
            System.out.println(i);
        }
    }
}

观测线程状态---------Thread.getstate()方法

线程的六种状态:

  1. 新建状态(New)

    新创建完一个线程对象,但还没调用start方法的状态

  2. 运行状态(Runable)

    分为就绪状态(Ready)和运行中状态(Running)两种状态

    就绪状态(Ready):线程调用start方法后等待CPU调度的状态

    运行中状态(Running):线程的得到CPU调度后,执行程序代码的状态

  3. 阻塞状态(Blocked)

    被阻塞等待监听器锁定的线程状态,无法被CPU调度

  4. 等待状态(Waiting)

    等待其他线程完成指定动作的状态

  5. 超时等待(Timed_Waiting)

    等待其他线程执行动作后达到指定时间的状态

  6. 死亡状态(Terminated)

    已经执行完的线程的状态,无法再调用start启动

public class ThreadState {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("/////////////");
        });

        Thread.State state = thread.getState();
        System.out.println(state);//New

        thread.start();
        state = thread.getState();
        System.out.println(state);//Runable

        while (state != Thread.State.TERMINATED){
            Thread.sleep(100);
            state = thread.getState();//更新状态
            System.out.println(state);
        }
    }
}

线程的优先级---------getPriority、setPriority(int x)方法

  • 线程默认优先级为5,只能设置1-10
  • 线程调度器按照优先级决定线程的运行
  • 优先级高不一定先执行,也可能后执行
public class ThreadPriority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " ==> " + Thread.currentThread().getPriority());
    }
    public static void main(String[] args) {
    	//主线程默认优先级
        System.out.println(Thread.currentThread().getName() + " ==> " + Thread.currentThread().getPriority());
        ThreadPriority priority = new ThreadPriority();
		
        Thread t1 = new Thread(priority);
        Thread t2 = new Thread(priority);
        Thread t3 = new Thread(priority);
        Thread t4 = new Thread(priority);
        Thread t5 = new Thread(priority);
        Thread t6 = new Thread(priority);
        
        //先设置优先级在启动
        t1.setPriority(Thread.MIN_PRIORITY);//1
        t3.setPriority(Thread.MAX_PRIORITY);//10
        t4.setPriority(Thread.NORM_PRIORITY);//5
        t5.setPriority(3);
        t6.setPriority(8);

        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
    }
}

守护线程---------daemon

  • 线程分为用户线程守护线程
  • 虚拟机要确保用户线程执行完毕,而不用等待守护线程执行完毕
public class ThreadDaemon {
    public static void main(String[] args) {
        God god = new God();
        Youself youself = new Youself();

        Thread thread = new Thread(god);
        thread.setDaemon(true);//默认为false,用户线程

        thread.start();//保护线程启动
        new Thread(youself).start();
    }
}

//守护线程
class God implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println("守护");
        }
    }
}
//用户线程
class Youself implements Runnable{
    @Override
    public void run() {
        for (int i = 0;i < 36500;i++) {
            System.out.println("活着"+i);
        }
        System.out.println("========over========");
    }
}

线程同步---------synchronized

并发:同一个对象被多个线程同时操作

线程同步解决并发问题

队列+锁

  • 同步方法 synchronized:默认为this

  • 同步块synchronized(Obj){}:Obj同步监视器,变化的量

  • ReentrantLock可重入锁

synchronized和ReentrantLock区别

  1. ReentrantLock锁需要手动开关,synchronized锁会自动释放
  2. ReentrantLock只能锁代码块,synchronized可以锁代码块和方法体
  3. ReentrantLock性能更好,具有更好的扩展性
//模拟多个线程操作同一对象  不安全  抢票
public class ThreadDemo5 implements Runnable{

    private int ticketNums = 10;//票数
    boolean flag = true;//标志位判断是否有票
/*
	//利用lock锁解决bing'f问题
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (flag) {
            try {
                lock.lock();
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }
*/
    @Override
    public void run() {
        while (flag) {
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        ThreadDemo5 threadDemo5 = new ThreadDemo5();

        new Thread(threadDemo5,"学生").start();
        new Thread(threadDemo5,"老师").start();
        new Thread(threadDemo5,"黄牛").start();

    }
    private synchronized void buy() throws InterruptedException {
    //private void buy() throws InterruptedException {
        //判断是否有票
        if (ticketNums <= 0){
            System.out.println("没票了");
            flag = false;
            return;
        }
        //延时
        Thread.sleep(1000);
        //买票
        System.out.println(Thread.currentThread().getName() + "买到了第" + ticketNums-- + "张票");

    }
}
/*
ArrayList 线程不安全
	1.数组越界
	2.比预期size小
	3.一个位置多次赋值或未赋值
CopyOnWriteArrayList 线程安全
*/
public class ThreadArrayList {
    public static void main(String[] args) throws InterruptedException {
        ArrayList<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                //synchronized (list){
                    list.add(Thread.currentThread().getName());
                //}
            }).start();
        }
        Thread.sleep(10000);
        System.out.println(list.size());
    }
}

死锁

多个线程相互占用资源,僵持住了

死锁四个必要条件

  1. 互斥:任意时刻资源只能被一个线程占用
  2. 请求与保持:线程提出新的资源请求,又对已经占用资源不释放
  3. 不可抢占:已经被占用的资源不能被其他线程抢占,只能主动释放
  4. 循环等待:一个线程占有了另一个线程需要的资源而又请求这个线程占有的资源

避免死锁的方法

线程协作(生产者消费者模式)

生产者不断生产,消费者不断消耗,生产者要在消费者消费了产品后再生产,消费者要在生产者生产完后再消费

  • wait():让线程等待,直到被唤醒,会释放锁
  • wait(long timeout):指定等待时间
  • notify():唤醒一个处于等待的线程
  • notifyall():唤醒同一个对象所有wait()等待的线程

都只能在同步方法和同步块中使用,负责会报错

解决方法

  1. 管程法:添加一个缓冲池,让消费者和生产者都在缓存池中操作

    //管程法
    public class ThreadPC {
        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 < 101; i++) {
                try {
                    container.push(new Chicken(i));
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                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 < 101; i++) {
                try {
                    container.Tack(new Chicken(i));
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("消费第" + i + "只鸡");
            }
        }
    }
    //产品
    class Chicken{
        int id;
    
        public Chicken(int id) {
            this.id = id;
        }
    }
    //缓存区
    class SynContainer {
        //缓存区大小
        Chicken[] chickens = new Chicken[10];
        int count = 0;//容器计数器
        //生产者放入产品
        public synchronized void push(Chicken chicken) throws InterruptedException {
            Thread.sleep(1000);
            if (count == chickens.length - 1){//如果容器满了,让生产者等待,消费者消费
                this.wait();
            }
            //放入产品
            chickens[count] = chicken;
            count++;
            //通知消费者消费
            this.notifyAll();
        }
        //消费者去出产品
        public synchronized Chicken Tack(Chicken chicken) throws InterruptedException {
            Thread.sleep(1000);
            if (count == 0){//如果容器空了,让消费者等待,生产者生产
                this.wait();
            }
            //取出产品
            count--;
            chickens[count] = chicken;
            //通知生产者生产
            this.notifyAll();
            return chicken;
        }
    
    }
    
  2. 信号灯法:添加一个标志位,当生产者生产了提醒消费者消费,当消费者消费了提醒生产者生产

//信号灯法
public class ThreadPC {
    public static void main(String[] args) throws InterruptedException {
        Chicken chicken = new Chicken();
        new Thread(()->{
            for (int i = 1; i < 101; i++) {
                System.out.println("生产了第" + i + "只鸡");
                try {
                    chicken.Product();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 1; i < 101; i++) {
                System.out.println("消费了第" + i + "只鸡");
                try {
                    chicken.Consume();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"B").start();
    }
}
//产品
class Chicken{
    int num = 0;//0:生产,1:消费
    public synchronized void Product() throws InterruptedException {
        if (num == 0){
            this.notifyAll();
            num++;
        }
        this.wait();
    }
    public synchronized void Consume() throws InterruptedException {
        if (num == 1){
            this.notifyAll();
            num--;
        }
        this.wait();
    }
}

线程池

提前创建多个线程资源,放入线程池中,便直接使用

//线程池
public class ThreadPool {
    public static void main(String[] args) {
        //1.创建服务,线程池 参数为线程数
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        //2.执行
        executorService.execute(new MyThread());
        executorService.execute(new MyThread());
        executorService.execute(new MyThread());
        executorService.execute(new MyThread());
        //3.关闭线程
        executorService.shutdownNow();
    }
}
class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
posted @ 2024-07-07 10:50  Rooki3  阅读(73)  评论(0)    收藏  举报