多线程学习

多线程学习

线程简介

程序、进程、线程

  • 程序:是指令和数据的有序集合、其本身没有任何运行的含义、是一个静态的概念
  • 进程:是执行程序的一次执行过程,他是一个动态的过程,是系统分配资源的单位
  • 线程:一个进程包含若干个线程,是CPU调度和执行的单位
注意:多线程只是模拟出来的,同一个时间,CPU只能执行一个线程,但由于执行速度过快,看作是同时执行。

线程的开辟

  1. 继承Thread类,重写run()方法,在测试类里创建实现类,在调用实现类的start()方法。
  2. 接上runnable接口,重写run()方法,用实现类实现接口,在用接口对象调用start()开辟线程。

实例龟兔赛跑

此实例实现了runnable接口,在重写的run()方法里用作对赛道的模拟,每进来一个线程就进行循环加1,实现距离的增加。通过Thread.currentThread().getName()获取进入赛道的角色,当循环次数达到100时跳出循环,即结束比赛。由于兔子需要模拟睡觉,可通过sleep()的方法实现线程休眠,注意是在run()里休眠,且判断胜负需要在休眠之前,不然会出现结束后还有在跑的现象。值得一提的是,通过简单的比较两线程的循环谁先达到100无法正确模拟谁是胜利者(胜利后另外一个还会继续跑),所以是将胜利者赋值给winner,然后在每次进入线程时判断胜利者是否为空,来判断游戏是否结束。

public class Race implements Runnable{//龟兔赛跑
    private static String winner = null;
public boolean gameOver(int step){//判断胜利者是否出现

    if (winner != null) return true;
        if (step>=100){
            winner = Thread.currentThread().getName();
            System.out.println(winner+"赢得了比赛");
            return true;
        }
        return false;
}


    @Override
    public void run() {

        for (int i = 1; i <= 100; i++) {//模拟赛道
            if (Thread.currentThread().getName().equals("兔子")&& i%10==0){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            boolean flag = gameOver(i);
            if (flag){//若为true,即出现胜利者,则游戏结束
                break;
            }

            System.out.println(Thread.currentThread().getName()+"跑了"+i+"米");
        }
    }
}

lambda表达式

接口的实现类

  1. 实现类

  2. 静态内部类

  3. 局部内部类

  4. 匿名内部类

  5. lambda表达式(是函数式接口即接口内只有一个方法)

    public class TextLambda {
        public static class Like2 implements ILike{
            @Override
            public void lambda() {//静态内部类
                System.out.println("I like lambda2");
            }
        }
    
        public static void main(String[] args) {
            class Like3 implements ILike{
                @Override
                public void lambda() {//局部内部类
                    System.out.println("I like lambda3");
                }
            }
            ILike like = new Like();
            like.lambda();
            ILike like1 =new Like2();
            like1.lambda();
            ILike like2 = new Like3();
            like2.lambda();
            ILike like3 = new ILike() {//匿名内部类
                @Override
                public void lambda() {
                    System.out.println("I like lambda3");
                }
            };
            ILike like4 = () -> {//无参lambda表达式,
                System.out.println("I like lambda4");
            };
            like4.lambda();
            ILove love =(int a,int b) -> {//有参
                System.out.println("I love lambda"+a);
            };
            love.lambda(99,32);
            ILove love1 = (a,b)->{//省略数据类型
                System.out.println("I love lambda"+a);
            };
            love1.lambda(99,13);//省略大括号(方法内只有一条语句),若有多条语句需要用代码块包裹
            ILove love2 = (a,b) -> System.out.println("I love lambda"+a);
            love2.lambda(99,123);
        }
    
    
    }
    
    interface ILike{//定义接口
        void lambda();
    }
    interface ILove{
        void lambda(int a, int b);
    }
    class Like implements ILike{//接口实现类
        @Override
        public void lambda() {
            System.out.println("I like lambda");
        }
    }
    
    

静态代理

  1. 代理,将对象1作为参数(此处为接口对象marry,那就可以将所有实现此接口的类当做对象传入),传到对象2,然后在对象2中使用对象1的方法,这样就在测试时,就可以只创建对象1而对象1本身不用干什么(即由对象2代理对象1的方法)
package staticproxy;

public class StaticProxy {
    public static void main(String[] args) {
        You you  =  new You();//真实对象
        //Thread对象为代理对象,并且Thread类是实现了Runnable接口的,Thread中的start()方法就类似于HappyMarry()方法一样
        new Thread(()-> System.out.println("我爱你")).start();
        WebbingCompany webbingCompany = new WebbingCompany(you);//代理对象
        webbingCompany.HappyMarry();
    }
}
interface marry{//接口(可看做所有实现类的父类)
    void HappyMarry();
};
class You implements marry{//实现类1

    @Override
    public void HappyMarry() {
        System.out.println("我要结婚了");
    }
}
class WebbingCompany implements marry{//实现类2
    private marry target;
    public WebbingCompany(marry target){//添加含参构造方法
        this.target = target;
    }

    @Override
    public void HappyMarry() {//帮成员变量结婚,结婚有婚前布置和收婚后尾款
        before();
        this.target.HappyMarry();
        after();
    }

    private void before(){
        System.out.println("布置场地");
    }
    private void after(){
        System.out.println("收尾款");
    }
}

线程生命周期

  1. 创建状态:当一个对象被创建(new)时线程就进入新生状态;

  2. 就绪状态:当调用start()方法时,就进入就绪状态,等待CPU调度;

  3. 运行状态:cpu调度,代码块开始运行;

  4. 堵塞状态:当调用wait(),sleep(),线程锁等使得代码块不继续执行,阻塞解除后就进入就绪状态,等待CPU调度

  5. 死亡状态:线程中断或者结束,一旦进入死亡状态就无法再次启动

    一些线程方法

    • setPriority(int newPriority) 更改线程的优先级
    • static void sleep( int milis) 使在指定的毫秒时间内让当前线程进入休眠,会抛出异常
    • void join() 强行加入线程(vip)直接插队·、
    • static void yield() 暂停当前线程,并执行其他线程(还得看CPU的重新调度)。
    • void interrupt() 中断线程,建议别用
    • boolean isAlive() 测试线程是否处于活跃状态

textyield()

package text;

public class TextYield 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) {
        TextYield textYield = new TextYield();
        new Thread(textYield,"A").start();
        new Thread(textYield,"B").start();
    }
}

textJoin()

package text;

public class TextJoin implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("vip来了------");
        }
    }

    public static void main(String[] args) {
        TextJoin textJoin = new TextJoin();
        Thread thread = new Thread(textJoin);
        thread.start();

        for (int i = 0; i < 100; i++) {
            if (i== 50){

                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("main "+i);
        }
    }
}

textState()

package text;//线程状态有NEW、RUNNABLE、TIMED_WAITING、TERMINATED

public class TextState extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        TextState thread = new TextState();
        Thread.State state = thread.getState();
        System.out.println(state);//NEW
        thread.start();
        state = thread.getState();
        System.out.println(state);//RUNNABLE
        while(state!= State.TERMINATED){
            try {
                sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            state = thread.getState();//每循环一次就检测并输出此线程的状态TIMED_WAITING
            System.out.println(state);//最后结束时TERMINATED
        }
    }
}

textyield()

package text;

public class TextYield implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始");
        Thread.yield();//进入线程的当前线程停止并退出,再等待CPU分配
        System.out.println(Thread.currentThread().getName()+"线程结束");
    }

    public static void main(String[] args) {
        TextYield textYield = new TextYield();
        new Thread(textYield,"A").start();
        new Thread(textYield,"B").start();
    }
}

textpriority()

package text;

public class TextPriority implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"的优先级是"+Thread.currentThread().getPriority());
    }

    public static void main(String[] args) {
        System.out.println("main的优先级"+Thread.currentThread().getPriority());//获得当前现成的优先级
        TextPriority textPriority = new TextPriority();
        Thread t1 = new Thread(textPriority,"t1");
        t1.setPriority(1);//设置优先级
        t1.start();

        Thread t2 = new Thread(textPriority,"t2");
        t2.setPriority(5);
        t2.start();

        Thread t3 = new Thread(textPriority,"t3");
        t3.setPriority(Thread.MAX_PRIORITY);
        t3.start();

    }
}

守护线程

线程分为用户线程和守护线程

  1. 用户线程是用户自己写的线程(如下面的thread1,thread2),守护线程是用下面方法修饰的线程
  • 用线程.setDaemon()方法将用户线程设置为守护线程。
  • 虚拟机必须确保用户线程全部执行完毕
  • 虚拟机不用等待守护线程结束
  • 例子有,gc线程(垃圾回收机制),后台操作日志,监控内存

线程同步

  • 线程安全问题:模拟上厕所问题,模型:人是一个线程对象,上厕所是run()方法。现在是有多个人上厕所(开辟了多个线程),对象1,进厕所准备上,但另一个人也进来了(共用一个线程池即厕所),就会造成线程安全问题

  • 解决方法:给厕所门(需要共享的)装上锁,一个人进去就锁门,等出来时再解锁,让另一个人进去。

  • 存在问题:属于同步线程,性能开销大,但保证了线程安全。

  • 加锁方式:方法体加锁—>在限定词后面直接加synchronized关键字

    ​ 代码块加锁—>格式为:synchronized(){ … }

    ​ **注:**小括号里写需要加锁的对象(一般为需要共享的对象)中括号为对

    ​ 此对象操作的代码块。

  • 例子:买票问题、银行取钱问题、集合存储问题。

    1.买票问题

package Unsafe;


public class UnSafeTicket {

    public static void main(String[] args) {

        Ticket ticket = new Ticket();//线程池,每个线程进来都可以操作里面类的属性(使票数减一)
            new Thread(ticket,"苦逼的我").start();//三个买票人。(代理)
            new Thread(ticket,"牛逼的你").start();
            new Thread(ticket,"可恶的黄牛党").start();
    }
}
class Ticket implements Runnable {
    private int TicketNums = 10;
    boolean flag = true;//外部控制

    @Override
    public void run() {
        while (flag) {
            buy();
        }
    }
    private synchronized void buy(){//方法体加锁
        if (TicketNums <= 0) {//先判断有没有票再决定买不买
            flag = false;
            return;
        }
        try {
            Thread.sleep(100);//休眠,放大问题
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "买了第" + TicketNums-- + "张票");//业务操做
    }
}

  1. 银行取钱问题
package Unsafe;

public class UnSafeBank {
    public static void main(String[] args) {
        Account account = new Account(50,"基金");//创建一个账户
        Bank bank = new Bank(account, 50,0);提交业务(银行卡,需要要取的金额,手头的钱数)
        new Thread(bank,"明哥").start();
        new Thread(bank,"明哥他妻子").start();
    }

}
class Account{
    int money;
    String id;
    public Account(int money,String id){
        this.money = money;
        this.id = id;
    }
}
class Bank implements Runnable{
    Account account ;
    int withMoney;
    int nowMoney;
    public Bank(Account account,int withMoney,int nowMoney){
        this.account= account;
        this.withMoney = withMoney;
        this.nowMoney = nowMoney;
    }

    @Override
    public void run() {
        synchronized (account) {//代码块加锁,account为需要进行共享的对象
            if (account.money - withMoney < 0) {
                System.out.println("余额不足,不能取了");
                return;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            account.money = account.money - withMoney;
            System.out.println(Thread.currentThread().getName() + "取了" + withMoney + ",余额还有" + account.money);

            nowMoney = nowMoney + withMoney;
            System.out.println(Thread.currentThread().getName() + "手里有" + nowMoney);

        }
    }
}

  1. 集合存储问题---------但在GUC里存在一个集合是线程安全的,CopyOnwriteArrayLIst<>().
package Unsafe;

import java.util.ArrayList;

public class UnSafeList extends Thread {
    public static void main(String[] args) {
        ArrayList<String> arrayList = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                synchronized(arrayList){//对代码块进行加锁,arrayList为需要加锁的对象
                    arrayList.add(Thread.currentThread().getName());}}
            ).start();

        }
        try {
            sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(arrayList.size());
    }
}

死锁

定义

多线程互相抱着对方所需要的资源,然后形成僵持,如例题中的($1)与($2),两进程刚进去分别占据mirror与lipstick,在没有得到下一个化妆品之前,都不释放锁,都得不到,就一直无法结束

代码

package lock;

public class MakeUp extends Thread {
    static Mirror mirror = new Mirror();
    static Lipstick lipstick = new Lipstick();
    String girlName ;
    int choice;
    MakeUp( String girlName ,int choice){
        this.choice= choice;
        this.girlName = girlName;
    }
    public void makeUp() throws InterruptedException {
        if (choice == 0) {
           ($1) synchronized (mirror) {//在这大括号里面的代码,在synchronized (lipstick)这串代码没有执行之前,是不会执行的
                System.out.println(this.girlName + "获得了" + "镜子");

            sleep(1000);
            synchronized (lipstick) {
                System.out.println(this.girlName + "获得了" + "口红");
            }
            }
        } else {
            ($2)synchronized (lipstick) {//在这大括号里面的代码,在synchronized (mirror)这串代码没有执行之前,是不会执行的
                System.out.println(this.girlName + "获得了" + "镜子");

            sleep(2000);
            synchronized (mirror) {
                System.out.println(this.girlName + "获得了" + "口红");
            }
            }
        }
    }
    public static void main(String[] args) {
            MakeUp makeUp = new MakeUp("美女",0);
            MakeUp makeUp1 = new MakeUp("帅哥",1);
            makeUp.start();
            makeUp1.start();
    }

    @Override
    public void run() {
        try {
            makeUp();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class Mirror{

}
class Lipstick{

}

lock

可重复锁

  1. synchronized使jvm花费较少的时间来调度线程,性能更好,并具有更好的扩展性
  2. lock只有代码锁
  3. lock是显示锁,有上锁(lock.lock();)解锁(lock.unlock();)synchronized是隐式锁,出来作用域直接释放锁
  4. 用try–catch–finally来加锁
package text;


import java.util.concurrent.locks.ReentrantLock;

public class TextLock {

    public static void main(String[] args) {

        Ticket ticket = new Ticket();
        new Thread(ticket,"苦逼的我").start();
        new Thread(ticket,"牛逼的你").start();
        new Thread(ticket,"可恶的黄牛党").start();
    }
}
class Ticket implements Runnable {
    private ReentrantLock lock = new ReentrantLock();
    private int TicketNums = 10;
    boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            buy();
        }
    }
    private  void buy(){
        lock.lock();
        try {
            if (TicketNums <= 0) {
                flag = false;
                return;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "买了第" + TicketNums-- + "张票");
        }catch (Exception e){
            e.printStackTrace();
        }finally{
            lock.unlock();
        }

    }
}

线程通信

  1. wait() 等待线程(此时也释放锁,不独占资源)
  2. noticfy() 通知其他线程即将wait()方法解除,继续执行wait()后面的代码

生产者消费者模式->管程法

  • 适合那种,生产完还能储存一些的(有数组储存数据)
package textPC;

import sun.rmi.runtime.NewThreadAction;

//生产者消费者模型--》管程法
//需要的类,生产者,消费者,产品,缓冲区
public class TextPC {
    public static void main(String[] args) {
        Container container = new Container();
        new Productor(container).start();
        new Customer(container).start();
    }

}
class Productor extends Thread{
    Container container;
    public Productor(Container container){
        this.container= container;
    }

    @Override
    public void run() {//只生产
        for (int i = 1; i <= 100; i++) {
            container.push(new Chicken(i));
            System.out.println("生产了第"+i+"只鸡");

        }
    }
}
class Customer extends Thread{
    Container container;
    public Customer(Container container){
        this.container= container;
    }

    @Override
    public void run() {//只消费
        for (int i = 1; i <= 100; i++) {
            System.out.println("消费了第"+container.pop().id+"只鸡");
        }
    }
}
class Chicken {//给鸡标号
    int id;

    public Chicken(int id) {
        this.id = id;
    }
}
class Container {//缓冲区

    Chicken[] chickens = new Chicken[10];//容器长度
    int count=0;//计数器
    public synchronized void push(Chicken chicken){//增加容量
        if (count==chickens.length){//如果容量满了,则等待线程(此时也释放锁,不独占资源)
            try {
                this.wait();//等待线程(此时也释放锁,不独占资源)
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //容量不满
        chickens[count]=chicken;//将传入的鸡加到容器里
        count++;


        this.notifyAll();//通知其他线程,有鸡可吃
    }
    public synchronized Chicken pop(){//减少容量
        if (count==0){//如果容器里没有鸡
            try {
                wait();//等待线程,同时释放锁,不占资源
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //若还有鸡可以消费
        Chicken chicken = chickens[count-1];//将容器里的鸡取出,涉及到数组是从零开始到九,则count—1
        count--;
        this.notifyAll();//通知其他线程,已经消费,可以补充了
        return chicken;
    }
}

生产者消费者模式->信号灯法

  • 适合那种,生产完就立即需要观看的(立即改变状态)
package textPC;

public class TextPC2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}
class Player extends Thread{//
    TV tv;
    public Player(TV tv){
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                tv.play("快乐大本营。。。");
            } else tv.play("广告。。。");
        }
    }
}
class Watcher extends Thread{
    TV tv;
    public Watcher(TV tv){
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            tv.watch();
        }

    }
}

class TV {//收纳演员的表演,并给观众看
    String voice;
    boolean flag = true;

    public synchronized void play(String voice) {
        if (!flag) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("演员表演了" + voice);
        notifyAll();
        this.voice = voice;//更新表演
        flag = !this.flag;//表演完就改变状态

    }

    public synchronized void watch() {
        if (flag) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观众观看了" + voice);

        notifyAll();
        flag = !this.flag;//看完就改变信号

    }
}

线程池

  1. 利用Executors工具类的newFixedThreadPool()方法创建线程池(可以创建多种返回值类型的线程池)
  2. ExecutorService是真正的线程接口,用来接收创建的线程池,这里运用了多态将接口对象实例化。
  3. 运用ExecutorService接口对象的excute(Runnable runnable)方法实现代理,最后用shutdown()方法关闭线程。
package threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPool {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);//创建接口对像,使用了多态
        Thread thread = new Thread("niu");//这样子无法定义线程的名字与下列三种的实现等同
        executorService.execute(thread);
        executorService.execute(new Ticket());
        executorService.execute(new Ticket());
        executorService.execute(new Ticket());
        executorService.shutdown();
    }

}
class Ticket implements Runnable{
    int  ticketNums = 10;

    @Override
    public void run() {
        while (true) {

            if (ticketNums <= 0) {
                break;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int temp;
            synchronized(this) {
                temp = --ticketNums;
            }
                System.out.println(Thread.currentThread().getName() + "买到第" + temp + "张票");
            }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值