java多线程学习笔记

多线程学习笔记

1.线程简介

1.1多任务

1.2多线程

java多线程学习笔记_第1张图片

1.3进程

在操作系统中运行的程序就是进程

​ 一个进程可以有多个线程,如视频中同时听声音,看图像,看弹幕,等等

1.4程序

​ 程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。

​ 进程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然有存在的意义。线程是CPU调度和执行的的单位。

注意

​ 很多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错局。

1.5核心概念

◆线程就是独立的执行路径;

◆在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;

◆main()称之为主线程,为系统的入口,用于执行整个程序;

◆在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能认为的干预的。

◆对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;

◆线程会带来额外的开销,如cpu调度时间,并发控制开销。

◆每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

2.线程创建

2.1三种创建方式

java多线程学习笔记_第2张图片

2.2实现Thread

java多线程学习笔记_第3张图片

//创建一个线程方法一:继承Thread类,重写run()方法,调用start开启线程
//注意:线程开启不一定立即调度,是由cup,交叉执行!
public class TestThread extends Thread {
     
    @Override
    public void run() {
     
        //run方法线程体
        for (int i = 0; i <20 ; i++) {
     
            System.out.println("我在看代码->"+i);
        }
    }

    public static void main(String[] args) {
     
        //main线程,主线程

        //创建一个线程对象
        TestThread testThread = new TestThread();
        //调用start(),开启线程
        /*
        * 交替执行
        */
        testThread.start();

        for (int i = 0; i <200 ; i++) {
     
            System.out.println("我在学习->"+i);
        }
    }
}

2.3实现Runnable(推荐)

java多线程学习笔记_第4张图片

package com.lby.多线程;

//创建一个线程
// 方法二:实现Runnable接口,重写run()方法,执行线程需要丢人Runnable接口实现类,调用run()

public class TestRunnable implements Runnable{
     
    @Override
    public void run() {
     
        //run方法线程体
        for (int i = 0; i <20 ; i++) {
     
            System.out.println("我在看代码->"+i);
        }
    }

    public static void main(String[] args) {
     
        //main线程,主线程

        //创建一个runnable线程对象
        TestRunnable testRunnable = new TestRunnable();

        //创建线程对象,通过线程对象来实现我们的线程代理
        Thread thread = new Thread(testRunnable);
        //调用start(),开启线程
        thread.start();

        for (int i = 0; i <200 ; i++) {
     
            System.out.println("我在学习->"+i);
        }
    }
}

2.4多个线程操作同一个对象

package com.lby.多线程;
//多个线程同时操作同一个对象
//买火车票的例子

//问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱。
public class Test implements Runnable{

    //票数
    private int ticketNum=10;

    @Override
    public void run() {
        while (true){
            if (ticketNum<=0){
                break;
            }
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread()+"-->拿到了第"+ticketNum--+"票");
        }
    }

    public static void main(String[] args) {

        Test ticket = new Test();

        new Thread(ticket,"小明").start();
        new Thread(ticket,"小红").start();
        new Thread(ticket,"小刚").start();
    }
}

2.5实现龟兔赛跑

java多线程学习笔记_第5张图片

//实现龟兔赛跑
public class Race implements Runnable{
     
    private  static  String winner;
    @Override
    public void run() {
     
        for (int i = 0; i <= 100; i++) {
     

            //模拟兔子睡觉
            if (Thread.currentThread().getName().equals("兔子")&&i/10==0){
     
                try {
     
                    Thread.sleep(1);
                } catch (InterruptedException e) {
     
                    e.printStackTrace();
                }
            }
            boolean gameOver = gameOver(i);
            if (gameOver){
     
                break;
            }
            System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
        }
    }
    private boolean gameOver(int step){
     
        //判断是否有胜利者
        if (winner!=null){
     
            return true;
        }{
     
            if (step>=100){
     
                winner=Thread.currentThread().getName();
                System.out.println("胜利者为:"+winner);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
     
        Race race = new Race();

        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    }
}

2.6实现Callable

java多线程学习笔记_第6张图片

3.静态代理

3.1静态代理模式

//静态代理模式总结:
    //真实对象和代理对象都要实现同一个接口
    //代理对象要代理真实角色

//好处:
    //代理对象可以做很多真实对象能不了的事情
    //真实对象专法能自己的事情

public class StacticProxy {
     
    public static void main(String[] args) {
     
        weddingCompany weddingCompany = new weddingCompany(new You());
        weddingCompany.HappyMarry();
    }
}

interface Marry{
     
    void HappyMarry();
}


//真实结婚,你去结婚
class You implements Marry{
     
    @Override
    public void HappyMarry() {
     
        System.out.println("开始结婚...");
    }
}
//代理角色,帮助你结婚
class weddingCompany implements Marry{
     

    private Marry taget;//真实目标角色!

    public weddingCompany(Marry taget) {
     
        this.taget = taget;
    }

    @Override
    public void HappyMarry() {
     
        before();
        this.taget.HappyMarry();//代理真实对象结婚
        after();
    }

    private void after() {
     
        System.out.println("结婚以后");
    }

    private void before(){
     
        System.out.println("结婚之前");
    }
}

4.Lamda表达式

java多线程学习笔记_第7张图片

4.1Lamda表达式

◆为什么要使用lambda表达式避免匿名内部类定义过多
可以让你的代码看起来很简洁。

◆去掉了一堆没有意义的代码,只留下核心的逻辑。

◆也许你会说,我看了Lambda表达式,不但不觉得简洁,反而觉得更乱,看不懂了。那是因为我们还没有习惯,用的多了,看习惯了,就好了。

◆理解Functional Interface(函数式接口)是学习Java8 lambda表达式的关键所在。

◆函数式接口的定义:

◆任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。◆对于函数式接口,我们可以通过lambda表达式来创建该接口的对象。

例子

/*
* 推倒函数式表达式
* */
public class TestLambda {
     

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

    public static void main(String[] args) {
     
        //第一种
        Ilike like = new Like();
        like.lambda();

        //第二种
        like = new Like2();
        like.lambda();


        //4.局部内部类
        class Like3 implements Ilike{
     

            @Override
            public void lambda() {
     
                System.out.println("I like Lambda3!");
            }
        }

        //第三种
        like = new Like3();
        like.lambda();
        //5.匿名内部类,没有类的名称,必须实现接口或者父类。
        like = new Ilike() {
     
            @Override
            public void lambda() {
     
                System.out.println("I like Lambda4!");
            }
        };
        //第四种
        like.lambda();

        //6.使用lambda简化
        like = () -> {
     
            System.out.println("I like Lambda5!");
        };
        //第五种
        like.lambda();
    }
}

//1.定义一个函数式接口
interface Ilike{
     
    void lambda();
}

//2.实现类
class Like implements Ilike{
     

    @Override
    public void lambda() {
     
        System.out.println("I like Lambda!");
    }
}

第二个例子


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

        Ilove love=(int a)->{
     
            System.out.println("love->"+a);
        };


        //简化1.参数类型
        love=(a)->{
     
            System.out.println("love->"+a);
        };
        //简化2.简化括号
        love=a -> {
     
            System.out.println("love->"+a);
        };
        //简化3.去掉花括号,多行不能简化
        love=a->System.out.println("love->"+a);

        love.love(29);

        //总结:
            //lambda表达式只能有一行代码的情况下才能简化成为一行,如果有多行,那么就用代码染包裹。
            //前提是接口为函数式接口
            //多个参教也可以去掉参数类型,要去掉就都去掉

    }
}

interface Ilove{
     
    void love(int a);
}

5.线程状态

5.1 5大状态

java多线程学习笔记_第8张图片

5.2 线程状态

java多线程学习笔记_第9张图片

5.3 线程方法

java多线程学习笔记_第10张图片

5.4 停止线程

//测试stop
//1.建议线程正常停止-—->利用次数,不建议死新环。
//2.建议使用标志位--->设置一个标志位
//3.不要使用stop或者destroy 等过时或者J0K不建议使用的方法

public class TestStop 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 stop(){
     
        this.flag=false;
    }

    public static void main(String[] args) {
     
        TestStop testStop = new TestStop();
        new Thread(testStop).start();

        for (int i = 0; i <2000 ; i++) {
     
            System.out.println("main->"+i);
            if (i==1000){
     
                //调用线程方法来停止
                testStop.stop();
                System.out.println("线程停止了!");
            }
        }
    }
}

5.5线程休眠

//模拟网络延时
public class TestSleep implements Runnable {
     
    //票数
    private int ticketNum=10;

    @Override
    public void run() {
     
        while (true){
     
            if (ticketNum<=0){
     
                break;
            }
            try {
     
                Thread.sleep(20);
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread()+"-->拿到了第"+ticketNum--+"票");
        }
    }

    public static void main(String[] args) {
     

        Test ticket = new Test();

        new Thread(ticket,"小明").start();
        new Thread(ticket,"小红").start();
        new Thread(ticket,"小刚").start();
    }
}

5.6模拟倒计时

import java.text.SimpleDateFormat;
import java.util.Date;

//模拟倒计时
public class TestSleep2 {
     
    public static void main(String[] args) {
     
        //打印当前系统时间
        Date date = new Date(System.currentTimeMillis());//获取当前时间
        while (true){
     
            try {
     
                Thread.sleep(1000);
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
                date=new Date(System.currentTimeMillis());
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            }
        }
    }
    //模拟倒计时
    public static void tenDown() throws InterruptedException {
     
        int nun=10;
        while (true){
     
            Thread.sleep(1000);
            System.out.println(nun--);
            if (nun<=0){
     
                break;
            }
        }
    }
}

5.7线程礼让

//测试礼让线程
//礼让不一定成功,看CPU心情
public class TestYield {
     
    public static void main(String[] args) {
     
        MyYield myYield = new MyYield();


        new Thread(myYield,"669").start();
        new Thread(myYield,"29").start();
    }

}
class MyYield implements Runnable{
     

    @Override
    public void run() {
     
        System.out.println(Thread.currentThread().getName()+"线程开始了");
        Thread.yield();//开始线程礼让
        System.out.println(Thread.currentThread().getName()+"线程停止了");
    }
}

5.8 join

//join表示插队
public class TestJoin 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 {
     
        //启动我们的线程
        TestJoin testJoin = new TestJoin();
        Thread thread=new Thread(testJoin);
        thread.start();
        for (int i = 0; i <1000 ; i++) {
     
            if (i==200){
     
                thread.join();
            }
            System.out.println("main->"+i);
        }
    }
}

5.9观察状态

java多线程学习笔记_第11张图片

//观察测试线程的状态
public class TestState {
     
    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);//Run

        while (state!=Thread.State.TERMINATED){
     //只要线程不终止,就一直输出
            Thread.sleep(1000);
            state=thread.getState();//更新线程状态
            System.out.println(state);
        }





    }
}

5.10线程优先级

java多线程学习笔记_第12张图片

public class TestPriority {
     
    public static void main(String[] args) {
     
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
        MyPriority myPriority = new MyPriority();

        Thread thread1 = new Thread(myPriority);
        Thread thread2 = new Thread(myPriority);
        Thread thread3 = new Thread(myPriority);
        Thread thread4 = new Thread(myPriority);
        Thread thread5 = new Thread(myPriority);


        //先设置默认级别,再启动
        thread1.start();

        thread2.setPriority(1);
        thread2.start();


        thread3.setPriority(4);
        thread3.start();


        thread4.setPriority(Thread.MAX_PRIORITY);
        thread4.start();

        thread5.setPriority(Thread.NORM_PRIORITY);
        thread5.start();
    }
}
class MyPriority implements Runnable{
     

    @Override
    public void run() {
     
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
    }
}

5.11 守护线程

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

◆虚拟机必须确保用户线程执行完毕。

◆虚拟机不用等待守护线程执行完毕。

◆如:后台记录操作日志,监控内存,垃圾回收等待。

//测试守护线程
public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        Peopel peopel = new Peopel();


        Thread thread = new Thread(god);
        thread.setDaemon(true);//默认false表示用户线程,正常线程都是用户线程

        thread.start();

        new Thread(peopel).start();
    }
}


//上帝
class God implements Runnable{

    @Override
    public void run() {
        while (true){
            System.out.println("上帝一直都在!");
        }
    }
}

//你

class Peopel implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            System.out.println("现在你"+i+"岁");
        }
        System.out.println("去世了!");
    }
}

6.线程同步

6.1并发

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

​ -处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象.这时候我们就需要线程同步.线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。

6.2队列和锁

注意安全性

◆由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待。

使用后释放锁即可。存在以下问题:
◆一个线程持有锁会导致其他所有需要此锁的线程挂
◆在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
◆如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒,置,引起性能问题

-不安全的买票例子

//不安全买票
public class UnsanfeBuyTicket implements Runnable{
     
    //票数
    private int ticketNum=10;

    Boolean falg=true;

    @Override
    public void run() {
     
        while (falg){
     
            try {
     
                buy();
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            }
        }
            System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNum--+"票");
        }
    private void buy() throws InterruptedException {
     
        if (ticketNum<=0){
     
            falg=false;
            return;
        }
        Thread.sleep(200);
    }

    public static void main(String[] args) {
     

        Test ticket = new Test();

        new Thread(ticket,"小明").start();
        new Thread(ticket,"小红").start();
        new Thread(ticket,"小刚").start();
    }
}

6.3同步方法

java多线程学习笔记_第13张图片

-正常取钱例子

//不安全买票
public class UnsanfeBuyTicket implements Runnable{
     
    //票数
    private int ticketNum=10;

    Boolean falg=true;

    @Override
    public synchronized void run() {
     
        while (falg){
     
            try {
     
                buy();
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            }
        }
        }
        //synchronized 同步方法,锁的是this对象
    private  void buy() throws InterruptedException {
     
        if (ticketNum<=0){
     
            falg=false;
            return;
        }
        Thread.sleep(2000);
        System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNum--+"票");
    }

    public static void main(String[] args) {
     

        Test ticket = new Test();

        new Thread(ticket,"小明").start();
        new Thread(ticket,"小红").start();
        new Thread(ticket,"小刚").start();
    }
}

6.4同步块

java多线程学习笔记_第14张图片

6.5JUC

import java.util.concurrent.CopyOnWriteArrayList;
//测试JUc安全类型到的集合
public class TestJUC {
     
    public static void main(String[] args) throws InterruptedException {
     
        CopyOnWriteArrayList list=new CopyOnWriteArrayList();

        for (int i = 0; i <10000 ; i++) {
     
            new Thread(()->{
     
                list.add(Thread.currentThread().getName());
            });
        }
        Thread.sleep(3000);
        System.out.println(list.size());
    }
}

6.7死锁

​ 多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形.某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题。

-死锁例子

//死锁:多个线程互相抱着对方需要的资源,然后形成僵持。
public class DeadLock {
     
    public static void main(String[] args) {
     
        Makeup makeup = new Makeup(0,"灰姑娘");

        Makeup makeup2 = new Makeup(1,"红姑娘");


        makeup.start();
        makeup2.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 (InterruptedException e) {
     
            e.printStackTrace();
        }
    }

    private void makeup() throws InterruptedException {
     
        if (choice==0){
     
            synchronized (lipstick){
     //获得口红的锁
                System.out.println(this.girlname+"获得口红的锁");
                Thread.sleep(1000);
                synchronized (mirror){
     
                    System.out.println(this.girlname+"获得镜子的锁");

                }
            }
        }else {
     
            synchronized (mirror){
     //获得镜子的锁
                System.out.println(this.girlname+"获得镜子的锁");
                Thread.sleep(3000);
                synchronized (lipstick){
     
                    System.out.println(this.girlname+"获得口红的锁");

                }
            }
        }
    }
}

修改以后

//死锁:多个线程互相抱着对方需要的资源,然后形成僵持。
public class DeadLock {
     
    public static void main(String[] args) {
     
        Makeup makeup = new Makeup(0,"灰姑娘");

        Makeup makeup2 = new Makeup(1,"红姑娘");


        makeup.start();
        makeup2.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 (InterruptedException e) {
     
            e.printStackTrace();
        }
    }

    private void makeup() throws InterruptedException {
     
        if (choice==0){
     
            synchronized (lipstick){
     //获得口红的锁
                System.out.println(this.girlname+"获得口红的锁");
                Thread.sleep(1000);
            } synchronized (mirror){
     
                System.out.println(this.girlname+"获得镜子的锁");
            }
        }else {
     
            synchronized (mirror){
     //获得镜子的锁
                System.out.println(this.girlname+"获得镜子的锁");
                Thread.sleep(3000);
            }synchronized (lipstick){
     
                System.out.println(this.girlname+"获得口红的锁");

            }
        }
    }
}

产生死锁的四个必要条件:
1.互斥条件:一个资源每次只能被一个进程使用。
2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3.不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

上面列出了死锁的四个必要条件,我们只要想办法破其中的任意一个或多个条件就可以避免死锁发生

6.7Lock(锁)

◆从JDK5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当
◆java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。
锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象
◆ReentrantLock 类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。

import java.util.concurrent.locks.ReentrantLock;

//测试Lock锁
public class TestLock {
     

    public static void main(String[] args) {
     
        TestLock2 testLock2=new TestLock2();

        new Thread(testLock2,"669").start();
        new Thread(testLock2,"29").start();
        new Thread(testLock2,"666").start();
    }

}
class TestLock2 implements Runnable {
     

    //票数
    private int ticketNum = 10;

    //定义lock锁
    ReentrantLock Lock=new ReentrantLock();
    
    @Override
    public void run() {
     
        while (true) {
     
            try {
     
                Lock.lock();
                if (ticketNum > 0) {
     
                    try {
     
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
     
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread() + "-->拿到了第" + ticketNum-- + "票");

                } else {
     
                    break;
                }
            }finally {
     
                Lock.unlock();
            }
        }
    }
}

synchronized与Lock的对比

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

7.线程协作

7.1生产者消费者模式

线程通信
◆应用场景:生产者和消费者问题
◆假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费.
◆如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止.
◆如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止。
java多线程学习笔记_第15张图片
线程通信-分析
这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件。
◆对于生产者,没有生产产品之前,要通知消费者等待,而生产了产品之后,又需要马上通知消费者消费
◆对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费。
◆在生产者消费者问题中,仅有synchronized是不够的synchronized可阻止并发更新同一个共享资源,实现了同步synchronized不能用来实现不同线程之间的消息传递(通信)

线程通信
◆Java提供了几个方法解决线程之间的通信问题
java多线程学习笔记_第16张图片
注意:均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常llegalMonitorStateException
解决方式1
并发协作模型“生产者/消费者模式–>管程法
◆生产者:负责生产数据的模块(可能是方法,对象,线程,进程);
◆消费者:负责处理数据的模块(可能是方法,对象,线程,进程);
◆缓冲区:消费者不能直接使用生产者的数据,他们之间有个“缓冲区生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
java多线程学习笔记_第17张图片
解决方式2
◆并发协作模型“生产者/消费者模式->信号灯法

7.2管程法

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

import java.util.Properties;

//生产者,消费者,产品,缓冲区
public class TestPC {
     
    public static void main(String[] args) {
     
        SynContatiner synContatiner = new SynContatiner();

        new productor(synContatiner).start();
        new Consumer(synContatiner).start();

    }
}
//生成者
class productor extends Thread{
     

    SynContatiner contatiner;

    public productor(SynContatiner contatiner){
     
        this.contatiner=contatiner;
    }
    @Override
    public void run() {
     
        for (int i = 0; i <100 ; i++) {
     
            System.out.println("生产了->第"+i+"只鸡");
            contatiner.push(new Chicken(i));
        }
    }
}
//消费者
class Consumer extends Thread{
     
    SynContatiner contatiner;

    public Consumer(SynContatiner contatiner){
     
        this.contatiner=contatiner;
    }
    @Override
    public void run() {
     
        for (int i = 0; i <100 ; i++) {
     
            System.out.println("消费了->第"+contatiner.pop().id+"只鸡");
        }
    }
}
//产品
class Chicken{
     
    int id;
    public Chicken(int id) {
     
        this.id = id;
    }


}
//缓存区
class SynContatiner{
     
    //需要一个容器大小
    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 {
     
                this.wait();
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            }
        }
        //如果可以消费
        count--;
        Chicken chicken=chickens[count];

        //吃完了。通知生产者生产

        this.notifyAll();
        return chicken;
    }
}

7.3线程池

使用线性池

◆背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
◆思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。
可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。

◆好处:
◆提高响应速度(减少了创建新线程的时间)
◆降低资源消耗(重复利用线程池中线程,不需要每次都创建)
◆便于线程管理(.…)
◆corePoolSize:核心池的大小
◆maximumPoolSize:最大线程数
◆keepAlive Time:线程没有任务时最多保持多长时间后会终止

◆JDK5.0起提供了线程池相关Ae1,ExecutorServicelExecutors
◆ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor

◆ void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
◆Futuresubmit(Callable-T>task):执行任务,有返回值,一般又来执行Callable
◆void shutdown():关闭连接池
◆Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池

使用线性池例子

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

//测试线程池
public class TestPool {
     

    public static void main(String[] args) {
     
        //1.创建服务,创建线程池
        //newFixedThreadPool 参数为:线程池大小
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        
        //执行
        executorService.execute(new MyThread());
        executorService.execute(new MyThread());
        executorService.execute(new MyThread());
        executorService.execute(new MyThread());
        executorService.execute(new MyThread());

        //2.关闭连接
        executorService.shutdown();
    }
}


class MyThread implements Runnable{
     

    @Override
    public void run() {
     

        System.out.println(Thread.currentThread().getName());

    }
}

你可能感兴趣的:(java,开发语言,后端,多线程)