Java基础07-多线程

线程简介

  1. 任务
    1. 多任务(人同时做多件事情,看似同时进行,实际大脑在同一个时间只执行一件事情)
  2. 多线程(多条路同时执行)
    1. image
  3. 程序-进程-线程
    1. 一个程序开启一个进程,一个进程可以有多个线程(一个播放器进程:声音线程、图像线程、字幕线程)
  4. Process与Thread
    1. 程序:是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
    2. 进程:是执行程序的一次执行过程,是一个动态的概念,是系统资源分配的单位
    3. 在一个进程中可以包含若干个线程,一个进程至少有一个线程,不然进程没有实际存在的意义,线程是CPU调度和执行的单位
    4. 大多数的多线程是模拟出来的,真正的多线程是有多个cpu(如服务器),如果是模拟出来的多线程,在只有一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,cpu会在线程间进行切换,所有就有了多线程并行执行的错觉
  5. 核心
    1. 线程是独立的执行路径
    2. 在运行程序时,即使没有自己创建线程,后台也会有多个线程(主线程,gc线程(回收机制)等)
    3. main() 称为主线程,为系统的入口,用于执行整个程序
    4. 在一个进程中,若开辟了多个线程,线程的运行由调度器安排调度,调度器与操作系统紧密相关,先后顺序是不能人为的干预的
    5. 多个线程操作同一份资源时会存在资源抢夺问题,需要加入并发控制
    6. 多线程会带来额外的开销,如cpu调度时间,并发控制开销
    7. 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

线程实现

  1. 线程的创建
    1. Thread class
package com.gaopeng.thread;

public class Demo01 extends Thread{
    //重写run方法是为了,在其中编写自己创建线程要运行的方法
    public void run(){
        test();
    }
    public void test(){
        for (int i = 0; i < 200; i++) {
            System.out.println("线程1------"+i);
        }
    }
    public static void main(String[] args) {
        //继承了Thread之后自己也有了多线程的特性,可以创建线程使用start方法
        Demo01 thread = new Demo01();
        thread.start();
        for (int i = 0; i < 200; i++) {
            System.out.println("主程序"+i);
        }
    }
}

2. Runnable
package com.gaopeng.thread;

public class Demo02 implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 200; i++) {
            System.out.println("Runnable实现多线程----"+i);
        }
    }
}

package com.gaopeng.thread;

public class Demo03 {
    public static void main(String[] args) {
        Demo02 demo02 = new Demo02();
        Thread thread = new Thread(demo02);
        thread.start();

        for (int i = 0; i < 500; i++) {
            System.out.println("主程序------"+i);
        }
    }
}
//?为什么Thread也实现了Runable接口,然后new Thread(a);时又传入一个实现Runnbale的实例对象
//模拟抢票,,线程不安全
package com.gaopeng.thread;

public class Demo05 implements Runnable{
    private int ticket=10;
    @Override
    public void run() {
        //设置while循环 是为了模拟抢票的环境
        while (true){
            if(ticket<=0)break;
            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticket--+"张票");
        }
    }

    public static void main(String[] args) {
        Demo05 demo05 = new Demo05();
        new Thread(demo05,"康康").start();
        new Thread(demo05,"Bryan").start();
        new Thread(demo05,"Nuoji").start();
        //10张票被抢了两次,线程不安全
    }
}

//龟兔赛跑
package com.gaopeng.thread;

public class Demo06 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();
                }
            }
            System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
            //如果返回结果为false  说明还没有胜利者
            Boolean result = gameOver(i);
            if (result){
                break;
            }
        }
    }

    private Boolean gameOver(int steps){
        //如果winner不为空 说明有了胜利者
        if(winner!=null){
            return true;
        }else{
            //如果winner为空说明没有胜利者 判断是否到达终点,若到达赋值胜利者
            if(steps>=100){
                winner=Thread.currentThread().getName();
                System.out.println(winner + "胜利了");
            }
        }
        return false;
    }

    public static void main(String[] args) {
        Demo06 demo06 = new Demo06();
        new Thread(demo06,"兔子").start();
        new Thread(demo06,"乌龟").start();
    }
}
3. Callable
	1. 实现Callable接口,需要返回值类型
	2. 重写call方法,需要抛出异常,并设置返回值类型
	3. 创建目标对象
	4. 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);//创建线程池,1表示一个线程资源
	5. 提交执行:Future<Boolean> result1 = ser.submit(t1);
	6. 获取结果:boolean r1 = result1.get();
	7. 关闭服务:ser.shutdownNow();

静态代理

  1. 代理:有人帮你做一些事情
  2. lambda表达式:创建类实现接口和使用方法简化成-使用方法(但是到最后都没有指定类型了,那么到底实现了什么接口,为何能精准的使用其中的方法(答:指定了实现的类型为接口))

线程状态

  1. image

线程的礼让(yield)

  1. 礼让线程,让当前正在执行的线程暂停,但不阻塞
  2. 将线程从运行状态转为就绪状态
  3. 让cpu重新调度,礼让不一定成功,要看cpu的调度结果。
package com.gaopeng.yield;

public class TestYield {
    public static void main(String[] args) {
        TestYield1 testYield = new TestYield1();
        new Thread(testYield,"a").start();
        new Thread(testYield,"b").start();
        //运行结果:
        /*
        *   a线程,开始执行
            b线程,开始执行
            a线程,执行结束
            b线程,执行结束
        * */
    }
}


class TestYield1 implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程,开始执行");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"线程,执行结束");
    }
}

join合并线程(类似于插队)

package com.gaopeng.join;

public class Demo01 implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("插队线程"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Demo01 demo01 = new Demo01();
        Thread thread = new Thread(demo01);
        thread.start();
        for (int i = 0; i < 100; i++) {
            if (i==50){
                thread.join();//main线程阻塞
            }
            System.out.println("主线程执行"+i);
        }
    }
}

获取线程的状态

  1. new:尚未启动的线程处于此状态
  2. runnable:在Java虚拟机中执行的线程处于此状态
  3. blocked:被阻塞等待监听器锁定的线程处于此状态
  4. waiting:正在等待另一个线程特定动作的线程处于此状态
  5. timed_waiting:正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
  6. terminated:已退出的线程处于此状态
    (已完成的线程不能再次start)
package com.gaopeng.status;

public class Demo01 {
    //获取线程的状态
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("。。。。。。。");
        });


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

        thread.start();
        System.out.println(state);//runnable

        while (state!= Thread.State.TERMINATED) {
            System.out.println(state);//timed_waiting
            state=thread.getState();
        }
        System.out.println(state);//terminated
    }
}

线程的优先级

  1. Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行(注意:设置了优先级只是被cpu调度的概率大了一些,并不是一定,有可能cpu会调用优先级的线程就会出现性能倒置)
  2. 线程的优先级用数字表示,范围1~10
    1. Thread.NORM_PRIORITY=5;
  3. 获取优先级,改变优先级
    1. getPriority() setPriority(int x)
package com.gaopeng.priority;

public class Demo01 {
    public static void main(String[] args) {
        P p = new P();
        Demo01 demo01 = new Demo01();
        Thread t = new Thread(p,"main");
        Thread t1 = new Thread(p,"1");
        Thread t2 = new Thread(p,"2");
        Thread t3 = new Thread(p,"3");
        Thread t4 = new Thread(p,"4");
        Thread t5 = new Thread(p,"5");
        Thread t6 = new Thread(p,"6");

        t1.setPriority(10);//最高优先级
        t6.setPriority(9);
        t2.setPriority(1);

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

    }

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

守护线程

  1. 线程分为用户线程和守护线程
  2. 虚拟机必须确保用户线程执行完毕(main主函数)
  3. 虚拟机不用等待守护线程执行完毕(gc垃圾回收线程)
  4. 守护线程:后台记录操作日志,监控内存,垃圾回收等
package com.gaopeng.deamon;

public class Demo01 {
    public static void main(String[] args) {
        Person person = new Person();
        God god = new God();

        Thread godThread = new Thread(god);
        godThread.setDaemon(true);//默认为false 一般情况都是用户线程
        godThread.start();
        new Thread(person).start();

    }

}
class God implements Runnable{

    @Override
    public void run() {
        while (true){
            System.out.println("上帝永远存在");
        }
    }
}
class Person implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 30000; i++) {
            System.out.println("开心的活着,努力奋斗,奋斗");
        }
        System.out.println("goodbye");
    }
}

线程同步

  1. 多个线程操作同一个资源
  2. 并发问题:同一个对象被多个线程同时操作,如何保证安全问题
  3. 解决:队列,多个线程进行排队访问资源
  4. 线程同步:多个线程访问同一个对象时,某些线程想要修改这个对象,这个时候就需要实现线程同步,线程同步就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的线程池形成队列,等待前面的线程使用完毕,下一个线程再进行使用。
  5. 锁机制:由于同一个进程的多个线程共享同一块存储空间,再带来方便的同时,也带来了访问冲突的问题,为了保证数据在方法中被访问时的正确性,在访问时加入了锁机制,synchronized,当一个线程获得对象的排他锁,独占资源,其他线程必须等待,使用之后释放锁,下一个线程在获得锁进行操作。
    1. 一个线程持有锁会导致其他所有需要此锁的线程挂起
    2. 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换问题,和调度延时,引起性能问题
    3. 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题。

线程的不安全(线程的内存都是自己的,互不影响,(线程操作时将主内存的资源拷贝到自己的内存然后操作)这样操作同一个资源时就会发生问题)

package com.gaopeng.syn;

//案例1:买票不安全
public class Demo01 {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        new Thread(ticket,"1").start();
        new Thread(ticket,"2").start();
        new Thread(ticket,"3").start();
    }
}

class Ticket implements Runnable{

    private int ticket=10;
    private boolean flag=true;

    @Override
    public void run() {
        while (flag){
            buy();
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    void buy(){
        if(ticket<=0){
            flag=false;
        }
        System.out.println(Thread.currentThread().getName()+"买了第"+ticket--+"张票");
    }
}
package com.gaopeng.syn;
//案例2:取钱不安全
public class Demo02 {
    public static void main(String[] args) {
        Account account = new Account(100, "爱情基金");
        GetMoney getMoney = new GetMoney(account,100,"我");
        GetMoney getMoney2 = new GetMoney(account,500,"你");
        new Thread(getMoney).start();
        new Thread(getMoney2).start();
    }

}

class Account{
    private int money;
    private String name;
    public Account(int money,String name){
        this.money=money;
        this.name=name;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getMoney() {
        return money;
    }

    public String getName() {
        return name;
    }
}

class GetMoney extends Thread{

    Account account;
    private int getMoney;
    private String name;
    private int money;

    public GetMoney(Account account,int getMoney,String name){
        super(name);//父类的构造方法,要写在第一行
        this.getMoney=getMoney;
        this.account=account;
    }
    @Override
    public void run() {

        //取钱,账户金额减少,手里的钱增加
        if(account.getMoney()<=0){
            System.out.println("钱已取完");
            return;
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        account.setMoney(account.getMoney()-getMoney);
        money=getMoney;
        System.out.println("AccountMoney:"+account.getMoney());
        System.out.println(this.getName()+"money:"+money);//这里的name获取的是线程的名字,这个类继承了Thread类,可以使用他的方法
    }
}
package com.gaopeng.syn;

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

public class Demo03 {
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {list.add(Thread.currentThread().getName().toString());}).start();
        }
//        Thread.sleep(3000);
        System.out.println(list.size());//9916
        //两个线程操作同一个list地址时会进行覆盖操作,所以会线程不安全
    }
}

使用synchronized优化线程不安全问题

package com.gaopeng.syn2;

//案例1:买票不安全  对代码进行改造加上同步锁
public class Demo01 {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        new Thread(ticket,"1").start();
        new Thread(ticket,"2").start();
        new Thread(ticket,"3").start();
    }
}

class Ticket implements Runnable{

    private int ticket=10;
    private boolean flag=true;

    @Override
    public void run() {
        while (flag){
            try {
                Thread.sleep(1000);
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    synchronized void buy(){//变成了同步方法,一个线程操作完了之后其他的线程才能继续操作
        if(ticket<=0){
            flag=false;
            return;
        }
        System.out.println(Thread.currentThread().getName()+"买了第"+ticket--+"张票");
    }
}
package com.gaopeng.syn2;
//案例2:取钱不安全
public class Demo02 {
    public static void main(String[] args) {
        Account account = new Account(100, "爱情基金");
        GetMoney getMoney = new GetMoney(account,100,"我");
        GetMoney getMoney2 = new GetMoney(account,500,"你");
        new Thread(getMoney).start();
        new Thread(getMoney2).start();
    }

}

class Account{
    private int money;
    private String name;
    public Account(int money,String name){
        this.money=money;
        this.name=name;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getMoney() {
        return money;
    }

    public String getName() {
        return name;
    }
}

class GetMoney extends Thread{

    Account account;
    private int getMoney;
    private String name;
    private int money;

    public GetMoney(Account account,int getMoney,String name){
        super(name);//父类的构造方法,要写在第一行
        this.getMoney=getMoney;
        this.account=account;
    }
    @Override
    public void run() {

        synchronized (account){//锁的对象是变化的对象,是会产生增删改的对象,这里是账户被增删改
            //取钱,账户金额减少,手里的钱增加
            if(account.getMoney()<=0){
                System.out.println("钱已取完");
                return;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            account.setMoney(account.getMoney()-getMoney);
            money=getMoney;
            System.out.println("AccountMoney:"+account.getMoney());
            System.out.println(this.getName()+"money:"+money);//这里的name获取的是线程的名字,这个类继承了Thread类,可以使用他的方法

        }

    }
}
package com.gaopeng.syn2;

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

public class Demo03 {
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                synchronized (list) {//哪个对象会增删改,就对他加上锁
                    list.add(Thread.currentThread().getName().toString());
                    System.out.println("0");
                }
            }).start();
        }
        Thread.sleep(3000);//上面的线程有可能没有全部执行完,所以设置三秒延迟
        System.out.println(list.size());//9916
        //两个线程操作同一个list地址时会进行覆盖操作,所以会线程不安全
    }
}

同步方法synchronized

  1. 我们需要对方法出一套机制,这套机制就是synchronized关键字,它包括两种用法synchronized方法和synchronized块:同步方法:public synchronized void method(int args){}
  2. synchronized方法控制对对象的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程处于阻塞状态,方法一旦执行,就独占这个锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,然后执行方法
  3. 缺点:若将一个大的方法申明为synchronized将会影响效率
  4. 例如:image
  5. 同步块:synchronized(Obj){}
    1. Obj称之为同步监视器
      1. obj可以是任何对象,但是推荐使用共享资源作为同步监视器
      2. 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class(反射原理)
    2. 同步监视器的执行过程
      1. 第一个线程访问,锁定同步监视器,执行其中代码
      2. 第二个线程访问,发现台同步监视器被锁定,无法访问
      3. 第一个线程执行完毕,解锁同步监视器
      4. 第二个线程访问,发现同步监视器没有锁,然后锁定并访问

并发编程JUC

package com.gaopeng.syn2;

import java.util.concurrent.CopyOnWriteArrayList;

public class TestJUC {
    //自带锁机制的list 用于并发编程
    public static void main(String[] args) throws InterruptedException {
        CopyOnWriteArrayList<String> list=new CopyOnWriteArrayList<String>();
        for (int i = 0; i < 1000; i++) {
            new Thread(()->{
               list.add(Thread.currentThread().getName()) ;
            }).start();
        }

        Thread.sleep(3000);
        System.out.println(list.size());
    }
}

死锁问题

  1. 多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形,某一个同步块同时拥有"两个以上对象的锁"时,就可能发生死锁问题
  2. 产生死锁的必要条件
    1. 互斥条件:一个资源每次只能被一个进程使用
    2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
    3. 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
    4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
  3. 解决方案(不要进行锁嵌套),打破四个必要条件之一或多个就可以避免死锁的发生
  4. 制造死锁
package com.gaopeng.lock;

public class Demo01 {
    public static void main(String[] args) {
        new Thread(new Operate(0),"线程0").start();
        new Thread(new Operate(1),"线程1").start();

    }
}

//lipstick:口红
class Lipstick{

}
//mirror:镜子
class Mirror{

}

class Operate implements Runnable{
    //请static修饰,确认资源只有一份
    static Lipstick lipstick=new Lipstick();
    static Mirror mirror=new Mirror();
    int choose;

    public Operate(int choose){
        this.choose=choose;
    }

    @Override
    public void run() {
        try {
            operation();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    void operation() throws InterruptedException {
        if (choose==0){
            synchronized (lipstick){
                System.out.println(Thread.currentThread().getName()+"获得了口红");
                Thread.sleep(1000);
                synchronized (mirror){
                    System.out.println(Thread.currentThread().getName()+"获得了镜子");
                }
            }
        }else{
            synchronized (mirror){
                System.out.println(Thread.currentThread().getName()+"获得了镜子");
                Thread.sleep(2000);
                synchronized (lipstick){
                    System.out.println(Thread.currentThread().getName()+"获得了口红");
                }
            }
        }
    }
}

  1. 解决死锁(不进行锁嵌套)
package com.gaopeng.lock;

public class Demo01 {
    public static void main(String[] args) {
        new Thread(new Operate(0),"线程0").start();
        new Thread(new Operate(1),"线程1").start();

    }
}

//lipstick:口红
class Lipstick{

}
//mirror:镜子
class Mirror{

}

class Operate implements Runnable{
    //请static修饰,确认资源只有一份
    static Lipstick lipstick=new Lipstick();
    static Mirror mirror=new Mirror();
    int choose;

    public Operate(int choose){
        this.choose=choose;
    }

    @Override
    public void run() {
        try {
            operation();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    void operation() throws InterruptedException {
        if (choose==0){
            synchronized (lipstick){
                System.out.println(Thread.currentThread().getName()+"获得了口红");
                Thread.sleep(1000);

            }
            synchronized (mirror){
                System.out.println(Thread.currentThread().getName()+"获得了镜子");
            }
        }else{
            synchronized (mirror){
                System.out.println(Thread.currentThread().getName()+"获得了镜子");
                Thread.sleep(2000);

            }
            synchronized (lipstick){
                System.out.println(Thread.currentThread().getName()+"获得了口红");
            }
        }
    }
}

Lock锁

  1. 显示的添加锁,释放锁
package com.gaopeng.lock;

import java.util.concurrent.locks.ReentrantLock;

public class Demo02 {
    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 final ReentrantLock lock = new ReentrantLock();
    private int tickeyNum = 10;
    @Override
    public void run() {
        getTicket();
    }

    void getTicket(){

        try {
            lock.lock();//添加锁
            while (true){
                if(tickeyNum>0){
                    Thread.sleep(1000);
                    System.out.println(tickeyNum--);
                }else {
                    break;
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//释放锁,一般使用try catch 将unlock放入finally中
        }
    }
}

synchronized与Lock的对比

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

线程协作(生产者消费者模式-非23种设计模式)

package com.gaopeng.communication;

//测试:生产者消费者模型--->利用缓冲区解决:管程法
//需要对象:生产者,消费者,缓冲区,产品
public class Demo01 {
    public static void main(String[] args) {
        SynContainer synContainer = new SynContainer();
        new Productor(synContainer).start();
        new Customer(synContainer).start();
    }
}
//生产者
class Productor extends Thread{
    SynContainer synContainer;

    public Productor(SynContainer synContainer) {
        this.synContainer=synContainer;
    }

    //生产商品
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            synContainer.push(new Chicken(i));
            System.out.println("生产了,编号为"+i+"的鸡");
        }
    }
}
class Customer extends Thread{
    SynContainer synContainer;

    public Customer(SynContainer synContainer) {
        this.synContainer=synContainer;
    }

    //消费商品
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            Chicken pop = synContainer.pop();
            System.out.println("消费了,编号为"+pop.id+"的鸡");
        }
    }
}
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 chicken1){//生产者和消费者同时操作容器,需要进行同步操作
        //判断容器是否存满了商品
        if (count==chickens.length){
            //等待消费者消费
            try {
                this.wait();
//                return;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //生产商品
        chickens[count]=chicken1;
        count++;
        //告知消费者消费
        this.notifyAll();
    }

    public synchronized Chicken pop(){
        if (count==0){
            //等待生产者生产商品
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //消费商品
        count=count-1;
        Chicken chicken2 =chickens[count];
//        chickens[count]=null;
        this.notifyAll();
        return chicken2;
    }
}

线程通信问题

//案例2--->信号灯法
package com.gaopeng.communication;

//生产者消费者模式--->信号灯法
public class Demo02 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Actor(tv).start();
        new Audience(tv).start();
    }
}
//演员
class Actor extends Thread{
    TV tv;
    public Actor(TV tv){
        this.tv=tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if(i%2==0) {
                tv.perform("轩辕剑");
                System.out.println("表演了轩辕剑");
            }else {
                tv.perform("天龙八部");
                System.out.println("表演了天龙八部");
            }
        }
    }
}
//观众
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();
//            System.out.println("观看了"+watch);
        }
    }
}
//容器
class TV{
    private String voice;
    private boolean flag=false;

    //演员表演
    public synchronized void perform(String voice){
        if (this.flag){
            //等于false时演员表演,通知观众观看
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }else {
            //演员表演,观众等待
            this.voice=voice;
            this.flag=!this.flag;
            this.notifyAll();
        }
    }

    //观众观看
    public synchronized void watch(){
        if (this.flag){
            //观众观看
            System.out.println("观众观看了"+voice);
            this.flag=!this.flag;
            System.out.println(flag);
            this.notifyAll();
        }else{
            //通知演员表演
            try {
                System.out.println(flag);
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
//        return this.voice;
    }
}

线程池

  1. 背景:经常创建和销毁,使用量特别大的资源,比如并发情况下的线程,对性能影响很大
  2. 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中,可以避免重复创建和销毁,实现重复利用。
  3. 优点:
    1. 提高响应速度,节省了重复创建的时间
    2. 降低资源消耗,节省了重复创建重复销毁的资源
    3. 便于管理线程
      1. corePoolSize:核心池的大小
      2. maximumPoolSize:最大线程数
      3. keepAliveTime:线程没有任务时最长保留时间
package com.gaopeng.pool;

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

public class Demo01 {
    public static void main(String[] args) {
        //创建线程池:
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        executorService.execute(new Test());
        executorService.execute(new Test());
        executorService.execute(new Test());
        executorService.execute(new Test());
        //应该有如何获取,如何归还等操作,跟数据库连接池一样?
        //关闭线程池
        executorService.shutdown();
    }
}

class Test implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

高级主题

posted @ 2021-09-15 23:35  争取做百分之一  阅读(44)  评论(0)    收藏  举报