L6 多线程

进程、线程

概念:

进程:是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竞争计算机系统资源的基本单位。

线程:是进程的一个执行单元,是进程内科调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程。

一个程序至少一个进程,一个进程至少一个线程。

创建线程

方法一:自定义线程类继承thread类

1,自定义线程类继承thread类

2,重写run方法,编写线程执行体

3,创建thread类的子类的对象。即刚定义的线程类

4,调用start方法启动线程

//示例代码:实现一个多线程同步下载图片
package L6;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;

//1,自定义线程类TestThread2继承thread类
public class TestThread2 extends Thread{
    private String UrlName;
    private String filename;
    public TestThread2(String UrlName,String filename){
        this.filename=filename;
        this.UrlName=UrlName;
    }
    //2,重写run方法
    //下载图片线程的执行体
    public void run(){
        WebDownloader w1=new WebDownloader();
        w1.Downloader(UrlName,filename);
        System.out.println("下载的文件名为"+filename);
    }

    public static void main(String[] args) {
        //3,创建thread类的子类的对象。
        TestThread2 T1=new TestThread2("https://pic1.zhimg.com/80/v2-6257d94a8a01c48fe57e6e6c4456c8ac_720w.jpg","美腿图片1.jpg");
        TestThread2 T2=new TestThread2("https://pic2.zhimg.com/80/v2-365543141033d69e5c8bc933bdbc2745_720w.jpg","美腿图片2.jpg");
        TestThread2 T3=new TestThread2("https://pic1.zhimg.com/80/v2-1937e5d15c49149c3ce2e908ce0d111c_720w.jpg","美腿图片3.jpg");
        //4,调用start方法启动线程
        T1.start();
        T2.start();
        T3.start();
    }
}
//下载器
class WebDownloader{
    //下载方法
    public void Downloader(String UrlName,String filename)
    {
        try {
            FileUtils.copyURLToFile(new URL(UrlName),new File(filename));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,d1方法出现问题");
        }
    }
}

备注:FileUtils.copyURLToFile(new URL(地址链接),new File(下载后保存的图片名));
使用方法:百度下载commons-io-2.8.0-bin.zip,解压后把commons-io-2.8.0.jar复制到项目的lib目录下(新建),对lib目录进行add as library操作。

方法二:自定义类实现Runnable接口

1,创建实现类 去实现Runnable接口

2,在实现类中重写run()方法,编写线程执行体

3,创建runnable接口的实现类对象,并传入Thread构造器,创建Thread类的对象

4,通过Thread类的对象调用start()开启线程

package L6;
//1,创建实现类 去实现Runnable接口
public class TestThread3 implements Runnable{
    public void run(){
        //2,在实现类中重写run()方法,编写线程执行体
        for (int i = 0; i < 500; i++) {
            System.out.println("我在看代码--"+i);
        }

    }
    public static void main(String[] args) {
        //3,创建runnable接口的实现类对象
        TestThread3 T3=new TestThread3();
        //创建线程对象,通过线程对象来开启我们的线程,代理
        //再把runnable接口的实现类对象T3丢入
        //Thread构造器的参数需要一个目标runnable实现类对象
        //Thread p=new Thread(T3);
        //p.start();   //此两行可以直接总结为以下一行
        new Thread(T3).start();    //将runnable接口的实现类对象,丢入Thread构造器,并用start()开启线程
        for (int i = 0; i < 500; i++) {
            System.out.println("我在学习多线程--"+i);
        }
    }
}

注:推荐使用方法二:Runnable接口的实现使得:“将任务和线程完全分离”,即:使得可以创建一个任务由多个线程来执行

方法三:匿名类创建线程

package L6;
//匿名内部类创建线程有两种方式
public class testThread5 {
    public static void main(String[] args) {
        方式1:相当于继承了Thread类,作为子类重写run()实现
        new Thread(){
            @Override
            public void run() {
                System.out.println("匿名内部类创建线程方式1...");
            }
        }.start();
        
        
        //方式2:实现Runnable,Runnable作为匿名内部类
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名内部类创建线程方式2...");
            }
        }){
        }.start();
    }
}

多个线程操作一个对象

示例代码:

package L6;
//多个线程操作同一个对象
//买火车票的例子
public class TestThread4 implements Runnable{
    //票数
    private int TicketNums=10;
    @Override
    //重写run方法
    public void run() {
        while (true){
            if(TicketNums==0){
                break;
            }
            try {
                //做一个延迟
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //Thread.currentThread().getName()返回对当前正在执行的线程对象的名字
            System.out.println(Thread.currentThread().getName()+"-->拿到了第"+(TicketNums--)+"张票");
        }
    }
    public static void main(String[] args) {
        TestThread4 ticket=new TestThread4();
        new Thread(ticket,"小红").start();
        new Thread(ticket,"小明").start();
        new Thread(ticket,"老李").start();
    }
}
//会发现问题,多个线程操作一个对象会造成数据紊乱

静态代理

  • 概念:

    代理模式:使用一个代理对象将对象包装起来,然后用该代理对象来取代该对象,任何对原始对象的调用都要通过代理,代理对象决定是否以及何时调用原始对象的方法

    静态代理:要求被代理类和代理类同时实现相应的一套接口,通过代理类调用重写接口的方法,实际上调用的是原始对象的同样的方法。

    public class StaticProxy {
        public static void main(String[] args) {
            you Y1=new you();
            WeddingCompany W1=new WeddingCompany(Y1);
            W1.HappyMarry();
        }
    }
    
    //定义结婚接口
    interface MARRY{
        //声明结婚方法
        void HappyMarry();
    }
    //真实角色,自己结婚。被代理类
    class you implements MARRY{
        @Override
        //实现MARRY接口中的HappyMarry()
        public void HappyMarry() {
            System.out.println("GQ要结婚了");
        }
    }
    //代理角色,帮助你结婚。代理类
    class WeddingCompany implements MARRY{
        //新建对象target来接接收传入的对象
        private MARRY target;    //target代表了所有MARRY接口的实现类 所实例化的对象。比如Y1
        public WeddingCompany(MARRY target){
            this.target=target;   //接收了传入对象,此时this.target就等于传入对象
        }
        //实现MARRY接口中的HappyMarry()
        @Override
        public void HappyMarry() {
            before();
            this.target.HappyMarry();//真实对象,此处所调用的方法也是传入对象所属类的方法
            after();
        }
        private void before(){
            System.out.println("结婚之前");
        }
        private void after(){
            System.out.println("结婚之后");
        }
    }
    
    

Lambda表达式

  • 函数式接口:

    1,任何接口,如果只包含唯一一个抽象方法,那么它就是个函数式接口。

    2,对于函数式接口,我们可以通过lambda表达式来创建该接口的对象。

  • Lambda表达式语法:

    左侧 : Lambda 表达式的参数列表

    右侧 : Lambda 表达式中所需执行的功能, 即 Lambda 体

    //示例代码
    1() -> System.out.println("Hello Lambda!");
    
    2(x) -> System.out.println(x)
        
    3,x -> System.out.println(x)         //若只有一个参数,小括号可以省略不写
    
    4//有两个以上的参数,有返回值,并且 Lambda 体中有多条语句
        Comparator <Integer> com = (x, y) -> {
        System.out.println("函数式接口");
        return Integer.compare(x, y);};
    
    
    public class LambdaTest2 {
        public static void main(String[] args) {
            //lambda简化
            Iliove L1;
            L1=(int a)->{System.out.println("i love lambda->"+a);};
            L1.lambda(250);
            //简化:去掉参数类型、小括号、和花括号
            L1=a->System.out.println("i love lambda->"+a);
            L1.lambda(520);
    
        }
    }
    //定义一个函数式接口Ilove
    interface Iliove{
        void lambda(int a);
    }
    

线程停止

  • 不推荐使用jdk提供的stop()、destroy()方法
  • 推荐使用一个标志变量。让线程自己停下来

线程休眠-sleep

  • 每个对象都有一个锁,sleep不会释放锁
  • sleep存在异常interruptedException
  • sleep时间达到后,线程进入就绪状态
public class testSleep {
    //给这个方法加上static,就能在main方法中直接调用
    public static void tendown() throws InterruptedException {
        int num=10;
        while (true){
            Thread.sleep(1000);
            System.out.println(num--);
            if(num==0){
                break;
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        tendown();
    }
}

线程礼让-yield

  • 礼让线程,能让当前正在执行的线程暂停,但并不阻塞

  • 将线程从运行状态转化为就绪状态

  • 让cpu重新调度,礼让不一定成功。看cpu心情。即礼让以后、几个线程重新竞争,随机进入

    public class TestYield {
        public static void main(String[] args) {
            //多个线程操作一个对象
            myyield M1=new myyield();
            new Thread(M1,"一号线程").start();
            new Thread(M1,"二号线程").start();
        }
    
    }
    class myyield implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"线程开始执行");
            Thread.yield();
            System.out.println(Thread.currentThread().getName()+"线程停止执行");
        }
    }
    

线程强制执行-join

  • 可以理解为插队

    package L6;
    //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 T1=new TestJoin();
            //此处不合并写成new Thread(T1).start();   因为需要对线程P做操作
            Thread P=new Thread(T1);
            P.start();
            //主线程
            for (int i = 0; i < 500; i++) {
                if (i==200){
                    //阻塞主线程,强制执行线程P
                    P.join();
                }
                System.out.println("main运行"+i);
            }
        }
    }
    

线程状态观测

线程五大状态:

  • 新建状态(New)

  • 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。

  • 阻塞(BLOCKED):表示线程阻塞于锁

  • 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。

  • 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。

  • 终止(TERMINATED):表示该线程已经执行完毕。

线程优先级

  • 获取优先级:P.getPriority() (P是线程对象)
  • 设置优先级权重:P.setPriority()
package L6;
public class TestPriority {
    public static void main(String[] args) {
        //主线程
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
        MyPriority M1=new MyPriority();
        //其他线程
        //多个线程操作一个实现类对象
        Thread T1=new Thread(M1);
        Thread T2=new Thread(M1);
        Thread T3=new Thread(M1);
        Thread T4=new Thread(M1);
        //先设置线程优先级、再启动线程
        T1.start();
        T2.setPriority(1);
        T2.start();
        T3.setPriority(5);
        T3.start();
        T4.setPriority(Thread.MAX_PRIORITY);
        T4.start();
    }
}
class MyPriority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
    }
}

守护线程

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

  • 虚拟机必须保证用户线程执行完毕,而不用等待守护线程执行完毕。用户线程结束后,虚拟机会停掉守护线程

  • 守护线程如:后台记录操作日志、监控内存、垃圾回收等等

    package L6;
    import jdk.internal.org.objectweb.asm.tree.MultiANewArrayInsnNode;
    public class TestDaemon {
        public static void main(String[] args) {
            God G1=new God();
            You Y1=new You();
            Thread T1=new Thread(G1);
            T1.setDaemon(true);      //false表示是用户线程,true表示是守护线程
            //守护线程启动
            T1.start();
            //用户线程启动
            new Thread(Y1).start();
        }
    }
    class God implements Runnable{
        @Override
        public void run() {
            while (true){
                System.out.println("上帝与你同在!");
            }
        }
    }
    class You implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 36500; i++) {
                System.out.println("你的一生都快乐的活着!");
            }
            System.out.println("挂了!");
        }
    }
    

解决线程安全1:同步代码块

  • 当多个线程操作一个对象时,会带来访问冲突和对象值混乱等问题。为了解决这个问题,加入了锁机制synchronized,当一个线程获得对象的排他锁时,会独占资源。其他线程必须等待。

  • synchronized机制:包括synchronized方法和synchronized块

  • synchronized块:(即同步代码块)

    synchronized(同步监视器){
        //需要被同步的代码
    }
    说明:1,需要被同步的的代码,就是操作共享数据的代码
         2,共享数据:多个线程共同操作的变量
         3,同步监视器,俗称:锁。任何一个类的对象,都可以作为锁
        	要求:多个线程必须共用同一把锁
    
    //通过同步代码块解决 实现runnable接口的线程安全问题
    public class windowTest1 {
        public static void main(String[] args) {
            window1 W1=new window1();
            //三个线程操作一个对象W1
            Thread T1=new Thread(W1);
            Thread T2=new Thread(W1);
            Thread T3=new Thread(W1);
            T1.setName("窗口1");
            T2.setName("窗口2");
            T3.setName("窗口3");
            T1.start();
            T2.start();
            T3.start();
        }
    }
    
    class window1 implements  Runnable{
        private int ticket=100;
        //主函数只创建了一个对象,所以obj只被创建了一次,所有线程共用这一个锁
        private Object obj=new Object();
        @Override
        public void run() {
            synchronized (obj){
                //以下都是需要被同步的代码
                while (true){
                    if(ticket>0){
                        System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+(ticket--));
                    }else {
                        break;
                    }
                }
            }
        }
    }
    
    
    
    
    
    //通过同步代码块解决 继承Thread类的线程安全问题
    public class windowTest2 {
        public static void main(String[] args) {
            //创建三个线程
            window2 W1=new window2();
            window2 W2=new window2();
            window2 W3=new window2();
    
            W1.setName("窗口1");
            W2.setName("窗口2");
            W3.setName("窗口3");
    
            W1.start();
            W2.start();
            W3.start();
        }
    }
    class window2 extends Thread{
        private int ticket=100;
        //加上static以后,不论new几个对象,所有线程都共用一个锁
        private static Object obj=new Object();
        @Override
        public void run() {
            synchronized (obj){
                //以下都是需要被同步的代码
                while (true){
                    if(ticket>0){
                        System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+(ticket--));
                    }else {
                        break;
                    }
                }
            }
        }
    }
    

解决线程安全2:同步方法

  • synchronized方法:即同步方法

    如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步

//通过同步方法解决 实现runnable接口的线程安全问题
public class windowTest3 {
    public static void main(String[] args) {
        window3 W1=new window3();
        //三个线程操作一个对象W1
        Thread T1=new Thread(W1);
        Thread T2=new Thread(W1);
        Thread T3=new Thread(W1);
        T1.setName("窗口1");
        T2.setName("窗口2");
        T3.setName("窗口3");
        T1.start();
        T2.start();
        T3.start();
    }
}
class window3 implements  Runnable{
    private int ticket=100;
    @Override
    //不可在此处加synchronized,因为while (true){}被包入同步区域的话,第一个进来的线程会循环到结束为止
    public void run() {
            while (true){
                show();
            }
    }
    synchronized void show(){   //此时同步监视器是this
        if(ticket>0){
            //线程休眠等于是个等待区,让几个线程都到达等待区,再进入下一步对共享数据的操作。这样可以放大线程中的问题所在
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+(ticket--));
        }
    }
}







//通过同步方法解决 继承thread的线程安全问题
public class windowTest4 {
    public static void main(String[] args) {
        //创建三个线程
        window4 W1=new window4();
        window4 W2=new window4();
        window4 W3=new window4();

        W1.setName("窗口1");
        W2.setName("窗口2");
        W3.setName("窗口3");

        W1.start();
        W2.start();
        W3.start();
    }
}
class window4 extends Thread{
    private static int ticket=100;
    @Override
    public void run() {
            //以下都是需要被同步的代码
            while (true){
                show();
            }
    }
    static synchronized void show(){   //同步监视器是window4.class
        if(ticket>0) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + (ticket--));
        }
    }
}

同步方法总结:

  • 同步方法依然涉及同步监视器,只是不需要显示声明
  • 非静态的同步方法,同步监视器是:this 即该类的对象
  • 静态的同步方法,同步监视器是:当前类本身 类.class
  • 在继承thread创建多线程的方式中,慎用this作为同步监视器。因为对象可能不唯一

死锁

概念:

  • 不同的线程分别占用对方所需要的同步资源不放弃,都在等待对方放弃。就形成了线程的死锁
  • 出现死锁以后,不会出现异常和提示,只是所有线程都处于阻塞状态,无法继续
//死锁示例1
public class DeadLock01 {
    public static void main(String[] args) {
        StringBuffer S1=new StringBuffer();
        StringBuffer S2=new StringBuffer();
        new Thread(){
            //此线程需要两把锁,先S1后S2
            @Override
            public void run() {
                synchronized (S1){
                    S1.append("a");
                    S2.append("b");
                    //休眠,提升死锁发生概率
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (S2){
                        S1.append("c");
                        S2.append("d");
                        System.out.println(S1);
                        System.out.println(S2);
                    }
                }
            }
        }.start();

        new Thread(new Runnable() {
             //此线程需要两把锁,先S2后S1
            @Override
            public void run() {
                synchronized (S2){
                    S1.append("E");
                    S2.append("F");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (S1){
                        S1.append("G");
                        S2.append("H");

                        System.out.println(S1);
                        System.out.println(S2);
                    }
                }
            }
        }).start();
    }
}


注:此代码出现死锁的情况。
   当一个线程占据S1锁,需要进入S2锁时。另一个线程占据S2锁,需要进入S1锁
   二者陷入对峙,形成死锁
//死锁示例2
package L6.Lock;
class A{
    public synchronized void foo(B b){   //监视器:A类的对象,此时为a
        System.out.println("当前线程名:"+Thread.currentThread().getName()+"进入了A实例的foo方法");
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("当前线程名:"+Thread.currentThread().getName()+"企图调用B实例的last方法");
        b.last();
    }
    public synchronized void last(){       //监视器:A类的对象,此时为a
        System.out.println("进入了A类的last方法内部");
    }
}

class B{
    public synchronized void bar(A a){       //同步监视器:b
        System.out.println("当前线程名:"+Thread.currentThread().getName()+"进入了B实例的bar方法");
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("当前线程名:"+Thread.currentThread().getName()+"企图调用A实例的last方法");
        a.last();
    }
    public synchronized void last(){    //同步监视器:b
        System.out.println("进入了B类的last方法内部");
    }
}

public class DeadLock02 implements Runnable{
    A a=new A();
    B b=new B();
    public void init(){
        Thread.currentThread().setName("主线程");
        //调用a对象的foo方法
        a.foo(b);
        System.out.println("进入主线程之后");
    }
    @Override
    public void run() {
        Thread.currentThread().setName("副线程");
        //调用b对象的bar方法
        b.bar(a);
        System.out.println("进入了副线程之后");
    }

    public static void main(String[] args) {
        //分线程,启动run方法
        DeadLock02 D=new DeadLock02();
        new Thread(D).start();
        //主线程
        D.init();
    }
}


分析:
    主线程,需要先握住锁a,进入a的同步方法foo。然后握住锁b,进入b的同步方法b.last()
    分线程,需要先握住锁b,进入b的同步方法bar。然后握住锁a,进入a的同步方法a.last()
    这样两个线程陷入对峙,互相阻塞,形成死锁

解决线程安全3:Lock锁

Lock与synchronized的异同?

  • 相同:二者都可以解决线程的安全问题

  • 不同:synchronized在执行完相应的同步代码以后,自动的释放锁。属于隐式锁

    ​ lock需要手动启动和手动结束。属于显式锁

package L6.Lock;

import java.util.concurrent.locks.ReentrantLock;

class window implements Runnable{
    //1,实例化一个ReentrantLock类的对象
    private ReentrantLock lock01=new ReentrantLock();
    private int ticket=100;
    @Override
    public void run() {
        while (true){
            try {
                //2,调用锁定方法lock。以下一块代码就变成了单线程
                lock01.lock();

                if(ticket>0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "售票,票号为:" + (ticket--));
                }else {
                    break;
                }
            } finally {
                //3,调用解锁方法unlock
                lock01.unlock();

            }
        }

    }
}

public class LockTest {
    public static void main(String[] args) {
        window W1=new window();
        Thread T1=new Thread(W1);
        Thread T2=new Thread(W1);
        Thread T3=new Thread(W1);
        T1.setName("窗口1");
        T2.setName("窗口2");
        T3.setName("窗口3");
        T1.start();
        T2.start();
        T3.start();
    }
}

同步机制课后练习:存钱

要求:

  • 两个储户分别向一个银行账户存钱。每次存一千,存三次
  • 每次存完打印账户余额
package L6.Lock;

public class accountTest {
    public static void main(String[] args) {
        //定义一个account类的对象,传入两个customer类的实现对象的构造器中。此时两个储户线程共用一个账户
        account acc=new account(0);
        customer c1=new customer(acc);
        customer c2=new customer(acc);
        c1.setName("甲");
        c2.setName("乙");
        c1.start();
        c2.start();
    }
}

class account{
    //1,account类中包含balance属性,可通过构造器赋值
    //2,account类中包含deposit方法
    private double balance;
    public account(double balance) {
        this.balance = balance;
    }
    public synchronized void deposit(double a){
        if(a>0) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            balance += a;
            System.out.println(Thread.currentThread().getName()+"存款成功,当前余额为:" + balance);
        }
    }
}
class customer extends Thread{
    //在customer类中定义一个account的属性,通过customer构造器传入。
    private account acc;
    public customer(account acc){
        //在构造器中实例化属性
        this.acc=acc;
    }
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            acc.deposit(1000);
        }
    }
}

线程通信,交替执行

  • wait():阻塞当前线程并释放当前的锁

  • notify():唤醒一个wait中的线程。如果有多个线程被wait,就唤醒优先级高的那个

  • notifyAll():唤醒所有wait中的线程

  • 注:这三个方法必须使用在同步方法或者同步代码块中

  • 实现交替的原理:

    当线程1拿到锁,然后完成共享数据操作以后,执行wait(),陷入阻塞,并解开线程1的锁

    线程1的锁解开以后,等待的线程2便可以进入,线程2拿到锁,执行了notify,唤醒了线程1,线程1循环至外部继续等待

    线程2在内部执行一样的操作,循环往复,交替执行

//线程通信示例1,实现两个线程交替打印1-100
public class communication {
    public static void main(String[] args) {
        number N1 = new number();
        Thread T2 = new Thread(N1);
        Thread T1= new Thread(N1);
        T1.setName("线程1");
        T2.setName("线程2");
        T1.start();
        T2.start();
    }
}

class number implements Runnable{
    private int num=1;
    @Override
    public void run() {
        while (true){
            synchronized (this) {
                //唤醒wait的线程
                notify();
                if (num < 100) {
                    System.out.println(Thread.currentThread().getName() + ":" + num++);
                    //wait:阻塞当前线程并且释放当前锁
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    break;
                }
            }
        }
    }
}

生产者消费者问题:

  • 生产者(productor)将产品交给店员(clerk),消费者(customer)从店员处取走产品
  • 店员一次只能取走固定数量的产品(定为20),如果生产者试图生产更多产品,店员会叫停生产者。
  • 如果店中产品小于20,店员会让生产者继续生产。如果店中没有产品,店员会让消费者等待至有产品
//线程通信示例2,生产者消费者问题 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值