java多线程


一、进程与线程

一个进程表示内存中一个独立的应用程序,每一个进程在内存中占有独立的内存空间;
一个线程代表进程中的一个环节,有独立的内存空间,线程之间可以相互切换,一个进程至少包含一个线程;

二、同步与异步,并发与并行

同步:线程同步执行,效率低,线程数据不安全;
异步:线程排队执行,线程数据安全;
并发:同一段时间内执行;
并行:同一时刻执行,同时执行;

三、创建线程

1、继承Thread类

重写run()方法,功能在run中实现

class MyThread extends Thread{
    @Override
    public void run(){
        for(int i=0;i<10;i++){
            System.out.println("我来了!"+i);
        }
    }
}

2、实现Runnable接口

采用Runnable接口创建任务的好处:
1、通过创建任务,然后给线程分配的方式实现多线程,解决多个线程同时执行相同任务的情况;
2、可以避免单继承所带来的局限性,允许多接口;
3、任务和线程本身是分离的,提高了线程的健壮性;
4、后续实现线程池技术,接受Runnable类型的任务,不接受Thread类型的线程;

public class MyRunnable implements Runnable{
    @Override
    synchronized public void run() {
        for (int i = 0; i <10; i++) {
            System.out.println("我来了"+i);
            System.out.println( Thread.currentThread().getName());
        }
    }
}

3、创建Callable对象

对象执行后有一个返回值,主线程可以通过返回值执行下一步操作。

public class ThreadDemo08 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Integer> c=new MyCallable();
        FutureTask<Integer> task=new FutureTask<>(c);
        new Thread(task).start();
        //主线程等待输出返回值
        System.out.println(task.get());
        for (int i = 0; i <10; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(i);
        }
    }
    static class MyCallable implements Callable<Integer>{
        @Override
        public Integer call() throws Exception {
            for (int i = 0; i <10; i++) {
                Thread.sleep(100);
                System.out.println(i);
            }
            return 100;
        }
    }
}

四、执行线程

1、开始线程

    //创建一个线程任务
    MyRunnable r=new MyRunnable();
    //创建一个进程,并为它分配一个任务
    Thread t=new Thread(r);
    //设置这个线程为守护线程,主线程结束,t也结束
    t.setDaemon(true);
    //执行这个线程
    t.start();

2、线程休眠

线程休眠500毫秒

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

3、线程中断

线程是否中断应该尤其本身决定,而不应从外部强行中断;

五、线程安全问题

多个线程在对同一数据进行操作时,可能会出现数据不安全问题,会出现不符合现实规则的数据。

1、同步方法

同步方法对买票的动作进行同步,使票的数量安全。

public class ThreadDemo04 {
    public static void main(String[] args) {
        Runnable runnable=new Ticket();
        new Thread(runnable).start();
        new Thread(runnable).start();
        new Thread(runnable).start();
    }
    static class Ticket implements Runnable{
        private int count=10;
        @Override
        public void run() {
            while (true) {
                boolean flag = sale();
                if (!flag)
                    break;
            }
        }
        public  synchronized boolean sale(){
            if(count>0){
                System.out.println("正在准备买票:");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println("出票成功,余票:"+count);
                return true;
            }else{
                return false;
            }
        }
    }
}

2、同步代码块

同步代码块采用锁的方式使买票的整个动作同步,任何对象都可以成为锁,需要同步的线程同步锁必须是同一把锁。

public class ThreadDemo03 {
    public static void main(String[] args) {
        //线程不安全
        Runnable runnable=new Ticket();
        new Thread(runnable).start();
        new Thread(runnable).start();
        new Thread(runnable).start();
    }
    static class Ticket implements Runnable{
        private int count=10;
        //同一把锁
        private Object o=new Object();
        @Override
        public void run() {
            while(true) {
                synchronized (o) {
                    if (count > 0) {
                        System.out.println(Thread.currentThread().getName()+"正在准备买票:");
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println("出票成功,余票:" + count);
                    }else{
                        break;
                    }
                }
            }
        }
    }
}

3、公平锁和非公平锁

公平锁:多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,都是队列的第一位才能得到锁。
非公平锁:多个线程去获取锁,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。

//传的参数为true,就表示公平锁
        private Lock l=new ReentrantLock(true);

4、显示锁

更好的体现锁和解锁的概念;
该代码在每一次执行while循环就加锁,执行完一次后就解锁;

public class ThreadDemo05 {
    public static void main(String[] args) {
        Runnable runnable=new Ticket();
        new Thread(runnable).start();
        new Thread(runnable).start();
        new Thread(runnable).start();
    }
    static class Ticket implements Runnable{
        private int count=10;
        //传的参数为true,就表示公平锁
        private Lock l=new ReentrantLock(true);
        @Override
        public void run() {
            while (true) {
                l.lock();
                if (count > 0) {
                    System.out.println("正在准备买票:");
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count--;
                    System.out.println("出票成功,余票:" + count);

                } else {
                    break;
                }
                l.unlock();
            }
        }
    }
}

5、线程死锁

线程1需要线程2返回的结果才能执行,线程2同时需要执行线程1的结果,线程1,线程2都得不到执行完毕的前提,于是线程死锁;

六、生产者和消费者

在生产的时候不能消费,就有了等待wait(),唤醒notify()的方法,才能保证数据在传输时候的安全;
示例中,用到了食物,生产者,消费者三个实体类,通过判断标记来确认是否可以执行对应线程;

public class ThreadDemo07 {
    public static void main(String[] args) {
        Food f=new Food();
        Cook cook = new Cook(f);
        cook.start();
        Waiter waiter = new Waiter(f);
        waiter.start();
    }
    static class Cook extends Thread{
        private Food f;
        public Cook(Food f){
            this.f=f;
        }
        @Override
        public void run(){
            for (int i = 0; i <100; i++) {
                if(i%2==0)
                    f.setNameAndTaste("小米粥","香辣");
                else
                    f.setNameAndTaste("老干妈小米粥","甜辣");
            }
        }
    }
    static class Waiter extends Thread{
        private Food f;
        public Waiter(Food f){
            this.f=f;
        }
        @Override
        public void run(){
            for (int i = 0; i <100; i++) {
                f.get();
            }
        }
    }

    static class Food{
        private String name;
        private String taste;
        //true表示可以生产
        private boolean flag=true;
        public String getName() {
            return name;
        }
        public  synchronized void setNameAndTaste(String name,String taste) {
            if (flag) {
                this.name = name;
                this.taste = taste;
                System.out.println("厨师生产的饭菜是:"+this.name+",味道是:"+this.taste);
                flag = false;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        public synchronized void get() {
            if (!flag) {
                System.out.println("服务员端走菜的名称是:" + name + ",味道是:" + taste);
                flag=true;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

七、Lambda表达式

public static void main(String[] args) {
    //冗余的Runnable代码
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("我来了!");
        }
    }).start();
    //实现同一功能的Lambda表达式,括号内还可以传入不同的参数
    Thread t=new Thread(()-> System.out.println("我来了"));
    t.start();
    }

八、线程池

线程池执行完任务后,在一定时间内不会关闭,等待线程任务;

1、缓存线程池

线程不会死亡,可以重复使用,节省创建和销毁线程的时间;

//缓存线程池
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "缓存线程池");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "缓存线程池");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "缓存线程池");
            }
        });
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "缓存线程池");
            }
        });

2、定长线程池

线程池的线程个数为定长,即最大同时进入的线程个数为线程长度

//定长线程池
        ExecutorService service1 = Executors.newFixedThreadPool(2);
        service1.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "定长线程池");
            }
        });
        service1.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "定长线程池");
            }
        });
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        service1.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "定长线程池");
            }
        });

3、单线程线程池

也就是一个线程的固定线程池,适用于需要异步执行但需要保证任务顺序的场景;

//单线程线程池
        ExecutorService service2 = Executors.newSingleThreadExecutor();
        service2.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "单线程线程池");
            }
        });
        service2.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "单线程线程池");
            }
        });

4、周期任务定长线程池

适用于定期执行任务场景,支持按固定频率定期执行和按固定延时定期执行两种方式;

//周期任务定长线程池
        ScheduledExecutorService service3 = Executors.newScheduledThreadPool(2);
        /**
         * 定时执行一次
         * 定时执行的任务
         * 时长数字
         * 时长数字的时间单位,TimeUnit的常量
         * */
        service3.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("周期");
            }
        }, 5, TimeUnit.SECONDS);
        /**
         * 周期性执行任务
         * 任务
         * 延时时长数字(第一次执行在什么时候以后)
         * 周期时长数字(每隔多久执行一次)
         * 时长数字的单位
         *
         * */
        service3.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("周期1");
            }
        },5,1,TimeUnit.SECONDS);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值