多线程基础学习

一.创建线程、实现线程

有两种方法第一种是继承Thread类,第二种是实现Runnable接口。

1、继承Tread类

​ 子类继承Thread类具备多线程功能

​ 启动线程:子类对象.start()

不建议使用:避免OOP单继承局限性

/*
线程的学习
总结:线程开启不一定立即执行,需要看cpu的调度安排
在执行线程的时候调用start()方法来启动子线程,
这时候子线程,和主线程是交替之行的。

如果调用run()方法来启动线程,则是先执行子线程,
之后再来执行主线程。
 */
public class TestThread1 extends Thread {
    @Override
    public void run() {
        try {
            sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (int i=0;i<10;i++)
            System.out.println("我在看代码---"+i);
    }

    public static void main(String[] args) {
        TestThread1 testThread1 = new TestThread1();
        testThread1.start();
        try {
            sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int j=0;j<20;j++)
            System.out.println("我在学习多线程---"+j);
    }
}

2、实现Runnable接口

​ 实现接口Runnable具有多线程能力

​ 启动线程:传入目标对象+Thread对象.start()

​ **推荐使用:避免单继承的局限性,灵活方便,方便同一个对象被多个线程使用 **

例:线程实现龟兔赛跑

import static java.lang.Thread.sleep;
/*
线程实现龟兔赛跑
 */
public class race implements Runnable {
    private static String Winner;

    @Override
    public void run() {
        for (int i=1;i<=100;i++){
            //人为的然兔子每10步休息0.01s
            if (Thread.currentThread().getName().equals("兔子")&&i%10==0) {
                try {
                    sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //胜利者已经存在
            if(GameOver(i)){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"-->走到了第"+i+"步");
        }

    }

    //判单是否完成比赛的
    private Boolean GameOver(int step){
        if(Winner!=null){
            return true;
        } else if (step>=100){
            Winner = Thread.currentThread().getName();
            System.out.println("Winner is "+Winner);
            return true;
        }
        return false;
    }


    public static void main(String[] args) {
        race race= new race();
        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    }
}

3、实现Callable接口

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

public class TestCallable  implements Callable <Boolean> {
    private String url;
    private String name;

    public TestCallable(String url,String name){
        this.url=url;
        this.name=name;
    }

    @Override
    public Boolean call() {
        new WebDownloder().download(url,name);
        System.out.println("下载的图片名字为:"+name);
        return true;
    }

    public static void main(String[] args) {
        TestCallable t1 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fa4.att.hudong.com%2F27%2F67%2F01300000921826141299672233506.jpg&refer=http%3A%2F%2Fa4.att.hudong.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1614909229&t=d6c07074e94ca4465416b37e14f5cef0","1.jpg");
        TestCallable t2 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fa0.att.hudong.com%2F52%2F62%2F31300542679117141195629117826.jpg&refer=http%3A%2F%2Fa0.att.hudong.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1614909230&t=6a6a762cda2412af43daff37427ecf4e","2.jpg");
        TestCallable t3 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fa2.att.hudong.com%2F27%2F81%2F01200000194677136358818023076.jpg&refer=http%3A%2F%2Fa2.att.hudong.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1614909230&t=7016dfebce1f6b2db216e6f84c8693b7","3.jpg");

       //创建执行服务  创建了3条线程
        ExecutorService service = Executors.newFixedThreadPool(3);

        //提交执行
        Future<Boolean> r1 = service.submit(t1);
        Future<Boolean> r2 = service.submit(t2);
        Future<Boolean> r3 = service.submit(t3);

        //获取结果
        try {
            boolean rs1 = r1.get();
            boolean rs2 = r2.get();
            boolean rs3 = r3.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        //关闭服务
        service.shutdownNow();
    }
}

class WebDownloder{
    public void download(String url,String name) {
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("Io异常");
        }
    }
}

4、静态代理模式

/*
总结:目标对象和代理对象要实现同一个接口
 */
public class StaticProxy {
    public static void main(String[] args) {
        WeeddingCompany w1 = new WeeddingCompany(new You());
        w1.HappyMarry();
    }
}
interface Marry {
    void HappyMarry();
}

//真是角色
class You implements Marry {
    @Override
    public void HappyMarry() {
        System.out.println("小张要结婚!!!");
    }
}

//代理角色
class WeeddingCompany implements Marry {

    private Marry target;
    public WeeddingCompany(Marry target) {
        this.target = target;
    }

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

    private void befroe(){
        System.out.println("布置婚礼现场!!!");
    }

    private void after(){
        System.out.println("收结婚彩礼!!!");
    }
}

5、Lamda表达式

/*
lambda表达是推到
 */
public class TestLamda {

    //3.静态内部类
    static class like implements ILkie{
        @Override
        public void lambda() {
            System.out.println("I like lambda2");
        }
    }

    public static void main(String[] args) {
        ILkie like = new Like();
        like.lambda();

        ILkie like1 = new like();
        like1.lambda();

        //4.局部内部类
        class Like2 implements ILkie {
            @Override
            public void lambda() {
                System.out.println("I like lambda3");
            }
        }
        like = new Like2();
        like.lambda();

        //5.匿名内部类,没有类的名称,必须借助接口或者父类
        like = new ILkie() {
            @Override
            public void lambda() {
                System.out.println("I like lambda4");
            }
        };
        like.lambda();

        //6.lambda表达方式
        like = ()->{
            System.out.println("I like lamnbda5");
        };
        like.lambda();
    }
}

//1.定义一个接口
interface ILkie {
    void lambda();
}

//2.实现类
class Like implements ILkie {
    @Override
    public void lambda() {
        System.out.println("I like lambda1");
    }
}
public class testlambda2 {
    public static void main(String[] args) {
        ILove love =null;
/*       //1.lambda表示简化
        ILove love = (int a)->{
            System.out.println(a+b);
        };
        //简化1,参数类型简化
        love = (a)->{
            System.out.println(a);
        };
        //简化2,简化括号
        love = a->{
            System.out.println(a);
        };*/
        //简化3,去掉花括号
        love = a -> System.out.println(a);
        love.love(521);

        /*
        总结:lambda表达式只有一行代码的时候才能简化成一行
        前提是接口为函数是接口
        多个参数也可以去掉参数类型,要去掉都去掉,必须加上一个括号
         */

    }
}
interface ILove{
    void love(int a);
}

二. 线程同步(并发)

1、synchronized与Lock的对比

  • Lock是显示锁(手动开启和关闭,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放。
  • Lock只有代码快锁,synchronized有代码快锁和方法锁。
  • 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性(提供更多的子类)
  • 优先使用顺序:Lock > 同步代码块(已经进入了方法体,分配了相应资源)> 同步方法(在方法体之外)

2、 死锁

/*
死锁   死锁产生的四个条件
1.互斥条件:一个资源每次只能被一个进程使用
2.请求与保持条件:一个进程因请求资源而阻塞,对已获得的资源保持不放
3.不剥夺条件:进程以获得的资源,在未使用完之前,不能强行剥夺
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
 */
public class DeadLock {
    public static void main(String[] args) {
        MakeUp g1 = new MakeUp(0,"晓丽");
        MakeUp g2 = new MakeUp(1,"晓梅");

        g1.start();
        g2.start();
    }
}

class Lipstick{//口红
}

class Mirror{//镜子
}

class MakeUp extends Thread{
    //所需要的资源只有一份用static来修饰,保证只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice;//选择
    String girlName;//女孩的姓名

   MakeUp(int choice,String girlName){
        this.choice = choice;
        this.girlName =girlName;
    }

    @Override
    public void run() {
        //化妆
        try {
            makeup();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //化妆 互相持有对方的锁,就是需要得对方的资源
    private void makeup() throws Exception{
        if (choice == 0){
            synchronized (lipstick){//获得口红的锁
                System.out.println(this.girlName+"持有口红锁");
                Thread.sleep(1000);
                //这样就造成了死锁
              /*  synchronized (mirror){//一秒钟后想要获得镜字锁
                    System.out.println(this.girlName+"获得镜子的锁");
                }*/
            }
            //解决办法
            synchronized (mirror){//一秒钟后想要获得镜字锁
                System.out.println(this.girlName+"获得镜子的锁");
            }
        } else{
            synchronized (mirror){//获得镜子的锁
                System.out.println(this.girlName+"持有镜子锁");
                Thread.sleep(2000);

             /*   synchronized (lipstick){//两秒钟后想要获得口红锁
                    System.out.println(this.girlName+"获得口红的锁");
                }*/
            }
            synchronized (lipstick){//两秒钟后想要获得口红锁
                System.out.println(this.girlName+"获得口红的锁");
            }
        }
    }
}

3、lock锁


import java.util.concurrent.locks.ReentrantLock;
//测试lock锁
public class TestLock {
    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 {
    int tichetNum = 10;
    //定义lock锁
    private final ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            BuyTicket();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
    private void BuyTicket(){
        while(true){
            try {
                lock.lock();//加锁
                if (tichetNum > 0){
                    System.out.println(Thread.currentThread().getName()+"抢到了第"+tichetNum--+"张票!!");
                } else {
                    break;
                }
            } finally {
                lock.unlock();//解锁
            }
        }
    }

}

4、生产者与消费者模型

在这里插入图片描述

4.1管程法

//测试:生产者消费者模型-->利用缓冲区解决:管程法
//生产者、消费者、产品、缓冲区

public class TestPC {
    public static void main(String[] args) {
        SynComtainer comtainer = new SynComtainer();

        new Producer(comtainer).start();
        new Consumer(comtainer).start();
    }
}

//生产者
class Producer extends Thread {
    SynComtainer comtainer;
    public Producer (SynComtainer comtainer){
        this.comtainer = comtainer;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("生产了"+i+"鸡");
            comtainer.push(new Chicken(i));
        }
    }
}

//消费者
class Consumer extends Thread {
    SynComtainer comtainer;
    public Consumer (SynComtainer comtainer){
        this.comtainer = comtainer;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("开始消费");
            System.out.println("消费了--->"+comtainer.pop().id+"鸡");
        }
    }
}

//产品
class Chicken{
    int id;

    public Chicken(int id) {
        this.id = id;
    }
}

//缓冲区
class SynComtainer {
    //缓冲区最多存放10个产品
    Chicken[] chickens = new Chicken[10];
    int count = 0;//存放产品的个数

    //生产者生产产品
    public synchronized void push(Chicken chicken){

        //System.out.println("----->"+count);
        //判断缓冲区是否满了,如果满了, -停止生产,开始消费
        if(count ==chickens.length-1){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果可以,就生产产品放入缓冲区
        chickens[count] = chicken;
        count++;
        this.notifyAll();//唤醒同一个对象上所调用wait()方法的线程,优先级别高得先执行
    }

    //消费者消费产品
    public synchronized Chicken pop(){

        //如果产品被消费完了就通知生产者开始生产
        if (count==0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //如果可以,就消费
        count--;
        Chicken chicken = chickens[count];

        this.notifyAll();
        return chicken;
    }
}

4.2 信号灯法

//测试生产者消费者模式:信号灯发,通过标志位来解决

public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Performer(tv).start();
        new Audience(tv).start();
    }

}

//生产者->演员
class Performer extends Thread{
    TV tv;
    public Performer(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if(i%2==0){
                tv.Play("快乐大本营播放中!!!");
            } else {
                tv.Play("抖音-->记录美好生活!!!");
            }
        }
    }
}

//消费者->观众
class Audience extends Thread{
    TV tv;
    public Audience(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}

//产品->节目
class TV{

    String program;//节目
    Boolean flag = true; //T为生产节目,F为播放节目

    public synchronized void Play(String program){
        if(flag==false){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("演员表演了:"+program);
        this.notifyAll();//通知唤醒观看线程
        this.program = program;
        this.flag = false;
    }

    public synchronized void watch(){
        if(flag==true){
            try {
                this.wait();//线程等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("观众观看了:"+program);
        this.notifyAll();
        this.flag = true;
    }
}

5、线程同步之synchronized锁

/*
不安全的取钱
synchronized 默认锁的是类本身
synchronized (){} 同步块可以锁任何对象
锁一定要锁变化的量。
加上synchronized是为了让线程更加安全
 */
public class UnSafeBank {
    public static void main(String[] args) {
        Account account = new Account(100,"儿子上学基金");
        Bank you = new Bank(account,80,"you");
        Bank girlfriend = new Bank(account,100,"girlfriend");

        you.start();

        girlfriend.start();

    }
}

class Account {//账户
    int balanceMoney; //余额
    String name; //姓名

    public Account(int balanceMoney, String name) {
        this.balanceMoney = balanceMoney;
        this.name = name;
    }
}

/*
模拟在银行里边取钱
 */
class Bank extends Thread {

    private int drawingMoney; //需要取出的钱
    private int NowMoney; //现在手里的钱
    Account account;//账户

    public Bank (Account account,int drawingMoney,String name){
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    public void run(){

        //synchronized 关键字是为了让线程变得安全
        synchronized (account){
            //判断卡里的余额是否够
            if(account.balanceMoney-drawingMoney<0){
                System.out.println(Thread.currentThread().getName()+"钱不够,取不了了");
                return ;
            }
            //模拟网络延时放大问题发生的概率
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //卡里剩余的余额 = 余额 - 取出的钱
            account.balanceMoney = account.balanceMoney-drawingMoney;
            //现在手里的钱
            NowMoney = NowMoney + drawingMoney;

            //Thread.currentThread().getName() <=> this.getName;
            System.out.println(this.getName()+"取走的钱为:"+NowMoney);
            System.out.println(account.name+"余额为:"+account.balanceMoney);
        }
        }

}

6、不安全的list

import java.util.ArrayList;
import java.util.List;

import static java.lang.Thread.sleep;
/*
不加sleep可能导致其它线程没有跑完,但是主线程跑完了。
这也就是为啥把sleep去掉之后加上synchronized而导致不够10000
 */
public class UnSafeList {
    public static void main(String[] args) {
        List <String> list = new ArrayList<String>();
            for (int i = 0; i < 10000; i++) {
                new Thread(() -> {
                    synchronized (list) {
                        list.add(Thread.currentThread().getName());
                    }
                }).start();
            }
        try {
            sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值