• 笔记-迎难而上之Java基础进阶4


    内部类创建多线程

    //匿名内部类创建多线程
    public class InnerClassThread{
    	public static void main(String[] args){
    		//回忆一下之前创建多线程的过程
    		//先创建一个Thread的子类,通过这个子类,重写run方法
    		//创建子类的对象.通过子类对象调用 start();方法开启多线程
    		//现在试一下匿名内部类创建多线程
    		new Thread(){
    			public void run(){
    				for (int i=0;i<10;i++){
    					System.out.println(Thread.currentThread().getName()+"---"+i);
    				}
    			}
    		}.start();
    		
    		//之前通过Runnable开启多线程的步骤
    		//创建一个Runnable的实现类,创建这个实现类的对象
    		//通过线程的构造方法传递实现类对象,开启线程
    		Runnable r = new Runnable(){
    			public void run(){
    				for (int i=0;i<10;i++){
    					System.out.println(Thread.currentThread().getName()+"---"+"程序员");
    				}
    			}
    		};
    		new Thread(r).start();
    	}
    }
    

    多线程的安全问题

    //实现Implements接口,是开启多线程的前提
    public class TicketDemo implements Runnable {
    	int ticket = 100;
    	public void run() {
    		while(true){
    			if(ticket>0){
    				try{
    				Thread.sleep(10);
    				}catch(Exception e){
    					e.printStackTrace();
    				}
    				
    				//需要看一看是那一条线程抢到CPU的执行权
    				System.out.println("我是"+Thread.currentThread().getName()+"正在出售第"+ticket+"票");
    				ticket--;
    			}
    		}
    	}
    }
    
    public class TicketObject{
    	public static void main(String[] args){
    		TicketDemo td = new TicketDemo();
    		//传递实现implement接口的对象
    		Thread t1 = new Thread(td);
    		Thread t2 = new Thread(td);
    		Thread t3 = new Thread(td);
    		t1.setName("吕布");
    		t2.setName("典韦");
    		t3.setName("关羽");
    		//开线程,让他们互相抢夺CPU
    		t1.start();t2.start();t3.start();
    	}
    }
    

    img

    这里出现了0,-1的票,出现了线程安全问题

    线程安全问题的解决方法_同步代码块

    格式:

    ​ synchronized(锁对象){

    共享数据的代码

    }

    注意事项:

    1. 通过代码块中的所对象,可以使用任意的对象
    2. 但是必须保证多个线程使用同一个对象
    3. 锁对象的作用是把同步代码块锁住,只让一个线程在同步代码块中执行
    //就在共享数据里加一个synchronized同步代码块就可以解决多线程的安全问题了
    while(true){
    				synchronized(obj){
    					if(ticket>0){
    				try{
    				Thread.sleep(10);
    				}catch(Exception e){
    					e.printStackTrace();
    				}
    				
    				//需要看一看是那一条线程抢到CPU的执行权
    				System.out.println("我是"+Thread.currentThread().getName()+"正在出售第"+ticket+"票");
    				ticket--;
    			}
    				}
    			
    		}	
    

    同步代码块的原理

    使用了同一个锁对象,这个锁对象叫做同步锁,

    3个线程一起抢夺cpu的执行权,谁抢到谁执行run方法进行卖票

    吕布抢到了cpu的执行权就会执行run方法,遇到synchronized代码块,这个时候吕布会检查synchronized代码块是否有锁对象,有就会进入到同步中执行,如果没有就等待,不进入同步

    当执行完了同步代码块的程序就会归还锁对象,

    总结:同步中的线程,没有执行完毕不会释放锁,同步外的线程没有锁进不去同步

    并发与并行

    并发:是指cpu在同一个时间段内执行多个任务,交替执行任务

    并行:是指cup在同一个时刻执行多个任务,一下执行多个任务

    解决线程安全问题_Lock锁

    使用步骤:

    1. 在成员位置创建一个ReentrantLock对象

    2. 在可能会出现安全问题的代码钱调用Lock接口中的方法lock获取锁

    3. 在可能出现安全问题的代码后调用lock接口中的方法unlock释放锁

      Lock l = new ReentrantLock();
      	public void run() {
      			while(true){
      				l.lock();
      					if(ticket>0){
      				try{
      				Thread.sleep(10);
      				//需要看一看是那一条线程抢到CPU的执行权
      				System.out.println("我是"+Thread.currentThread().getName()+"正在出售第"+ticket+"票");
      				ticket--;
      				}catch(Exception e){
      					e.printStackTrace();
      				}finally{
      					l.unlock();
      				}
      

    线程的等待唤醒机制

    什么是等待机制?

    一个线程进行了规定操作后,就进入等待状态wait,等待其他线程执行完他们的指定代码过后,再将其唤醒notify,

    wait:线程不在活动,不在参与调度

    notify:线程会唤醒正在wait的线程

    注意事项

    1. wait方法与notify方法必须要由同一个锁对象调用,因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程
    2. wait方法与notify方法属于Object类的方法
    3. wait方法与notify方法必须要做同步代码块或者同步函数中使用
    public class BaoZi {
        //包子是包子铺跟吃货的共享资源
        String pi;
        String xian;
        boolean flag = false;
    }
    
    //包子铺是一个线程类,生产包子,但是包子不能够生产太快(就是不可以让cpu全部被他占用了),
    // 包子过多,不会吃不完,所以需要一个等待与唤醒机制
    public class BaoZipu extends Thread{
        private BaoZi bz;
    
        public BaoZipu(BaoZi bz) {
            this.bz = bz;
        }
    
        public void run() {
            //重写run方法,设置线程任务,任务是生产包子
            int count = 0;
            while (true) {
                synchronized (bz) {
                    //如果有包子,就等待,不需要生产包子
                    if (bz.flag == true) {
                        try {
                            bz.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //没有包子,就生产包子
                    if (count % 2 == 0) {
                        bz.pi = "剥皮";
                        bz.xian = "三鲜馅";
                    } else {
                        bz.pi = "冰皮";
                        bz.xian = "牛肉大葱馅";
                    }
                    count++;
                    System.out.println("包子铺正在生产:" + bz.pi + bz.xian + "包子");
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //包子有了有就改为true状态
                    bz.flag = true;
                    //唤醒另外一条线程
                    bz.notify();
                    System.out.println("包子铺已经生产好了:" + bz.pi + bz.xian + "包子");
    
                }
            }
        }
    }
    
    
    public class ChiHuo extends Thread{
        private BaoZi bz;
        public ChiHuo(BaoZi bz){
            this.bz = bz;
        }
        public void run(){
            //重写run方法,设置线程任务,任务是吃包子
            while(true){
                synchronized (bz){
                    //没有包子,就等待包子的产生
                    if(bz.flag==false){
                        try {
                            bz.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //有包子就吃包子
                    System.out.println("吃货正在吃:"+bz.pi+bz.xian+"包子");
                    //吃完包子就把flag设置为false
                    bz.flag=false;
                    //唤醒线程另外一条线程
                    bz.notify();
                    System.out.println("吃货已经把:"+bz.pi+bz.xian+"的包子吃完了");
                    System.out.println("-------------");
                }
            }
        }
    }
    
    
    public class Demo {
        public static void main(String[] args) {
            BaoZi bz = new BaoZi();
            //开启线程,执行run方法
            new BaoZipu(bz).start();
            new ChiHuo(bz).start();
        }
    }
    
    

    进入TimeWaitting(等待计时)的两种方式

    1. 使用sleep(long m)方法,在毫秒值结束之后,线程睡醒进入到runnable/bloked状态
    2. 使用wait(long m)方法,wait方法如果在毫秒值结束之后,还没有被notify唤醒,就会自动进入Runnable/Blocked状态

    线程池

    线程池就是一个容器,可以使用集合来创建(ArrayList,HashSet,LinkedList,HashMap)

    当我们需要使用线程时可以从线程池中通过remove等方法取出来

    取出来后还可以返回去,通过add方法

    但是在JDK1.5之后提供了一个创建线程池的工厂类,用来生成线程池

    java.util.concurrent.Executors:线程池的工厂类

    Executors类中的静态方法:static ExcecutorService newFixedThreadPool(int nThreads) 创建一个可以重用固定线程数的线程池

    参数:int nThreads:创建线程池中包含的线程数量

    返回值:ExecutorService接口,返回的就是ExecutorService接口的实现类对象,我们可以使用ExecutorService接口接收

    线程池的使用步骤:

    1. 使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
    2. 创建一个类,实现Runnable接口
    3. 调用ExecutorService中的方法submit,传递线程任务
    4. 调用ExecutorService中的方法shutdown销毁线程池
    线程池的代码实现,
    //创建Runnable接口实现类,因为等下一下的submit();方法会使用到它
    public class RunnableRun implements Runnable{
    	public void run(){
    	System.out.println(Thread.currentThread().getName()+"");
    	}
    	
    }
    
    import java.util.concurrent.*;
    public class ThreadPool{
    	public static void main(String[] args){
    		//创建线程池工厂,通过类Executors的方法newFixedThreadPool();
    		//该类返回一个ExecutorService接口的实现类对象
    		ExecutorService es = Executors.newFixedThreadPool(2);
    		//使用ExecutorService类的方法submit()来开启线程
    		//submit类需要传递一个Runnable接口的实现类对象
    		//返回的结果是线程执行的任务
    		es.submit(new RunnableRun());
    		es.submit(new RunnableRun());
    		es.submit(new RunnableRun());
    		es.submit(new RunnableRun());
    	}
    }
    

    Lambda表达式

    面向对象的思想:

    ​ 做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情

    函数式编程思想:

    只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程

    体验Lambda的更优写法
    public class LambdaDemo{
    	public static void main(String[] args){
    		//匿名内部内创建多线程
    		new Thread(new Runnable(){
    			public void run(){
    				System.out.println(Thread.currentThread().getName()+"线程名称");
    			}
    		}).start();
    		//Lambda表达式创建多线程
    		new Thread(()->{
    				System.out.println(Thread.currentThread().getName()+"线程名称");
    			}
    		).start();
    	}
    }
    

    Lambda标准格式

    Lambda表达式的标准格式:
    由三部分组成:

    ​ a.一些参数 b.一个箭头 c.一段代码

    格式:(参数列表) -> {一些重写的代码}

    解析:

    ();接口中抽象方法的参数列表,没有参数,就空着,由参数就写参数

    -> :传递的意思,把参数传递给方法体{}

    ();重写接口中的抽象方法的方法体

    剩下几个Lambda表达式f的练习明天更……

  • 相关阅读:
    移动开发iOS&Android对比学习--异步处理
    PHP中文乱码解决办法[转]
    在eclipse里配置Android ndk环境 适用于windows mac 和linux[转]
    android在更新ADT以后报java.lang.NoClassDefFound的解决办法
    在Mac OS下配置PHP开发环境
    在iOS中使用百度地图
    简单说明CentOS源码安装程序
    SecureFXPortable中文乱码
    从Linux下载文件到Windows没有换行问题
    从Windows复制文件到Linux显示乱码问题
  • 原文地址:https://www.cnblogs.com/train99999/p/10921064.html
Copyright © 2020-2023  润新知