多线程篇章,线程进程概念,synchronized 锁,线程安全同步锁解决方案,多线程常用方法,线程池的相关用法,

说出进程的概念
进程:进程指在内存中正在运行的程序
说出线程的概念
线程:进程内部的一个独立执行单元(就是应用程序一个功能通向cpu的路径)
能够理解并发与并行的区别
并行:多个事件在同一时刻发生(同时发生)。
并发:多个事件在某个时间段内(交替)发生。

继承的方式开启新线程:
	public clsss MyThread extends Thread{
		//重写run方法,设置线程任务
		run(){
		//方法内部写线程任务
		}
	}
	main(){
		new MyThread().start();//开启新线程
	}

能够描述Java中多线程运行原理

在这里插入图片描述
调用start方法会开辟一个栈空间运行run方法
多个栈空间的方法都是独立的,互相不影响
能够使用继承类的方式创建多线程

public clsss MyThread extends Thread{
		//重写run方法,设置线程任务
		run(){

		}
	}
	main(){
		new MyThread().start();//开启多线程
	}

能够使用实现接口的方式创建多线程

public class RunnableImpl implements Runnable{
	//重写run方法,设置线程任务
	run(){
	}
}
new Thread(new RunnableImpl()).start();//开启多线程

能够说出实现接口方式的好处

	1.避免了实现类单继承的局限性(实现了Runnable接口,还可以继承其它的类,实现其它的接口使用)
	2.降低了设置线程任务和执行线程之间的耦合性,增强了扩展性

能够解释安全问题的出现的原因,电影院卖票的案例:
在这里插入图片描述
多线程访问了同一个共享的数据
能够使用同步代码块解决线程安全问题

synchronized(锁对象){
	可能出现安全问题的代码
	(访问了共享数据的代码)
}
注意:保证锁对象要唯一

能够使用同步方法解决线程安全问题
1.把访问共享数据的代码,提取出来放在一个方法中
2.在方法上增加一个同步关键字synchronized

 语法:
	修饰符 synchronized 返回值类型 方法名(参数列表){
		出现了安全问题的代码
		(使用了共享数据的代码)
	}

synchronized 锁的详解图:
在这里插入图片描述
JDK1.5之后,提供有锁的接口,可以有效监测锁的取和还,用多态new一个锁出来
Lock lock = new ReentrantLock();
使用:
在方法开头,加入取锁方法,lock.lock();
在方法结束,加入还锁方法,lock.unlock();
具体代码案例:

public class Ticket implements Runnable{
	private int ticket = 100;    
   
	Lock lock = new ReentrantLock();    
/*    
 * 执行卖票操作    
 */    
@Override    
	public void run() {    
		//每个窗口卖票的操作         
		//窗口 永远开启         
		while(true){        
			lock.lock();            
				if(ticket>0){//有票 可以卖            
					//出票操作                 
					//使用sleep模拟一下出票时间                 
						try {                
							Thread.sleep(50);                    
						} catch (InterruptedException e) {                
							// TODO Auto‐generated catch block                    
							e.printStackTrace();                    
						}                
					//获取当前线程对象的名字                 
					String name = Thread.currentThread().getName();                
					System.out.println(name+"正在卖:"+ticket‐‐);                
				}            
			lock.unlock();            
		}        
	}    
}

死锁:(拓展,理解:就是尽量不要写锁中锁,程序容易卡死)
同步锁使用的弊端:当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了其他的同步。这时容易引发一种现象:程序出现无限等待,这种现象我们称为死锁。这种情况能避免就避免掉。

能够说出线程6个状态的名称

在这里插入图片描述

	NEW(新建) 
	Runnable(可运行)
	Blocked(锁阻塞)
	Waiting(无限等待)
	TimedWaiting(计时等待,睡眠)
	Teminated(被终止/死亡)

能够理解等待唤醒案例
吃包子案例
一个生产者:生产者生产包子–>唤醒消费者吃包子
一个消费者:消费者吃完包子–>唤醒生产者生产包子
在这里插入图片描述
BaoZi.java

/*
    资源类:包子类
	属性:皮,陷,包子状态
 */
public class BaoZi {
    //皮
    String pi;
    //陷
    String xian;
    //包子状态 初始值为false没有包子
    boolean flag = false;
}

BaoZiPu.java

/*
    包子铺类:是一个线程类
        线程任务: 生产包子
        对包子的状态进行判断
          true:有包子
            包子铺线程调用wait等待
          false:没有包子
            包子铺线程开始生产包子
            生产x皮x陷的包子
            生产包子花费3秒钟
            生产完包子,修改包子的状态为true
            包子铺线程唤醒吃货线程,吃包子
    注意:
        1.生产包子和吃包子,只能有一个在执行,需要使用同步技术
        2.同步技术需要使用锁对象,可以使用包子对象
            在成员位置创建一个包子变量
            使用构造方法为包子变量赋值
 */
//包子铺类:是一个线程类
public class BaoZiPu implements Runnable{
    //在成员位置创建一个包子变量
    BaoZi bz;

    //使用构造方法为包子变量赋值
    public BaoZiPu(BaoZi bz) {
        this.bz = bz;
    }

    //线程任务: 生产包子
    @Override
    public void run() {
        //包子铺一直生产包子
        while (true){
            /*
            1.生产包子和吃包子,只能有一个在执行,需要使用同步技术
            2.同步技术需要使用锁对象,可以使用包子对象
         */
            synchronized (bz){
                //对包子的状态进行判断
                if(bz.flag == true){
                    //true:有包子 包子铺线程调用wait等待
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            /*
                包子铺被吃货唤醒之后执行的代码
             */
                //false:没有包子 包子铺线程开始生产包子
                //生产x皮x陷的包子
                bz.pi = "薄皮";
                bz.xian = "牛肉大葱陷";
                System.out.println("包子铺正在生产"+bz.pi+bz.xian+"的包子!");
                //生产包子花费3秒钟
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //生产完包子,修改包子的状态为true
                bz.flag = true;
                //包子铺线程唤醒吃货线程,吃包子
                bz.notify();//唤醒的是bz锁对象上等待的吃货线程
                System.out.println("包子铺已经生产好了"+bz.pi+bz.xian+"的包子,吃货赶紧来吃吧!");
            }
        }
    }
}

ChiHuo.java

/*
    吃货类:是一个线程类
        线程任务:吃包子
        对包子的状态进行判断
           false:没有包子
             吃货线程调用wait等待
           true:有包子
            吃货线程开始吃包子
            打印吃x皮x陷的包子
            吃完包子,修改包子的状态为false
            吃货线程唤醒包子铺线程,做包子
     注意:
        1.生产包子和吃包子,只能有一个在执行,需要使用同步技术
        2.同步技术需要使用锁对象,可以使用包子对象
            在成员位置创建一个包子变量
            使用构造方法为包子变量赋值
 */
//吃货类:是一个线程类
public class ChiHuo implements Runnable{
    //在成员位置创建一个包子变量
    BaoZi bz;

    //使用构造方法为包子变量赋值
    public ChiHuo(BaoZi bz) {
        this.bz = bz;
    }

    //线程任务:吃包子
    @Override
    public void run() {
        //吃货一直吃包子
        while(true){
            /*
            1.生产包子和吃包子,只能有一个在执行,需要使用同步技术
            2.同步技术需要使用锁对象,可以使用包子对象
         */
            synchronized (bz){
                //对包子的状态进行判断
                if(bz.flag==false){
                    //false:没有包子 吃货线程调用wait等待
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            /*
                吃货线程被包子铺线程唤醒之后执行的代码
             */
                //true:有包子 吃货线程开始吃包子
                //打印吃x皮x陷的包子
                System.out.println("吃货正在吃"+bz.pi+bz.xian+"的包子!");
                //吃完包子,修改包子的状态为false
                bz.flag = false;
                //吃货线程唤醒包子铺线程,做包子
                bz.notify();//唤醒bz对象上等待的包子铺线程
                System.out.println("吃货已经吃完了"+bz.pi+bz.xian+"的包子,包子铺赶紧生产包子吧!");
                System.out.println("------------------------------------------------------------");
            }
        }
    }
}

Demo01WaitAndNotify.java

/*
    等待与唤醒案例:
        Object类中的方法:
             void wait()
                在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
                被唤醒的线程,会执行wait方法之后的代码
             void notify() 唤醒在此对象监视器(对象锁)上等待的单个线程。
        注意:
            1.wait和notify方法必须由锁对象调用,而且必须是同一个锁
                锁对象-->wait()  锁对象-->notify()
            2.wait和notify方法一般都是写在同步中

     测试类:
        创建一个包子对象
        创建一个包子铺线程,生产包子
        创建一个吃货线程,吃包子

        包子铺正在生产薄皮牛肉大葱陷的包子!
        包子铺已经生产好了薄皮牛肉大葱陷的包子,吃货赶紧来吃吧!
        吃货正在吃薄皮牛肉大葱陷的包子!
        吃货已经吃完了薄皮牛肉大葱陷的包子,包子铺赶紧生产包子吧!
        ------------------------------------------------------------
        包子铺正在生产薄皮牛肉大葱陷的包子!
        包子铺已经生产好了薄皮牛肉大葱陷的包子,吃货赶紧来吃吧!
        吃货正在吃薄皮牛肉大葱陷的包子!
        吃货已经吃完了薄皮牛肉大葱陷的包子,包子铺赶紧生产包子吧!
        ------------------------------------------------------------
 */
public class Demo01WaitAndNotify {
    public static void main(String[] args) {
        //创建一个包子对象
        BaoZi bz = new BaoZi();
        //创建一个包子铺线程,生产包子
        new Thread(new BaoZiPu(bz)).start();
        //创建一个吃货线程,吃包子
        new Thread(new ChiHuo(bz)).start();
    }
}

多线程常用方法:

获取当前线程对象的名字
String name = Thread.currentThread().getName();
实现线程接口:
implements Runnable
让线程休眠多少100毫秒:
Thread.sleep(100)

线程池的相关用法:

使用线程池无返回值得用法:

/*
    线程池
    在JDK1.5的时候java提供了线程池
    java.util.concurrent.Executors类:线程池的工厂类,用来生产线程池
    静态方法:
        static ExecutorService newFixedThreadPool(int nThreads)
            创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
        参数:
              int nThreads:创建线程池中线程的个数
        返回值:
            ExecutorService:是生产线程池,类型是一个接口,newFixedThreadPool返回的就是ExecutorService接口的实现类对象
                注意:我们无需关注ExecutorService的实现类是谁,我们只需要会使用ExecutorService接口来接收这个实现类即可(多态)
                    这叫面向接口编程
    java.util.concurrent.ExecutorService:线程池
        Future<?> submit(Runnable task) 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
            把线程任务传递给submit方法,submit方法会在线程池中获取一个线程用于执行任务;执行完毕会自动把线程在归还给线程池
        Future<T> submit(Callable<T> task) 提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。
        void shutdown()  用于销毁线程池,一般不建议使用
 */
public class Demo01ThreadPool {
    public static void main(String[] args) {
        //1.使用线程池工厂类Executors提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
        ExecutorService ex = Executors.newFixedThreadPool(2);
        //2.调用线程池ExecutorService中的方法submit,传递线程任务,执行线程任务
        //new Thread(new Runnable(){}).start();
        ex.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"线程任务1执行了!");//pool-1-thread-2线程任务执行了!
            }
        });

        ex.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"线程任务2执行了!");//pool-1-thread-1线程任务执行了!
            }
        });

        ex.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"线程任务3执行了!");//pool-1-thread-2线程任务执行了!
            }
        });

        //void shutdown()  用于销毁线程池,一般不建议使用
        ex.shutdown();

        //线程池销毁之后,就在内存中消失了,就不能在执行线程任务了
        ex.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"线程任务3执行了!");//RejectedExecutionException
            }
        });
    }

}

使用线程池有返回值得用法:

/*
    Future<T> submit(Callable<T> task) 提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。
    java.util.concurrent.Callable<T>接口:用于设置线程任务
         V call() 计算结果,如果无法计算结果,则抛出一个异常。
         重写call方法,返回一个执行泛型类型的数据
 */
public class Demo02ThreadPool {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //1.使用线程池工厂类Executors提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
        ExecutorService ex = Executors.newFixedThreadPool(2);
        //2.调用线程池ExecutorService中的方法submit,传递线程任务,执行线程任务,接收线程任务的返回值
        Future<Double> f1 = ex.submit(new Callable<Double>() {
            @Override
            public Double call() throws Exception {
                return 1.1;
            }
        });
        System.out.println(f1);//java.util.concurrent.FutureTask@7006c658

        //使用Future接口中的方法V get()获取线程任务的返回值
        Double d = f1.get();
        System.out.println(d);


        Future<Integer> f2 = ex.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                //返回一个0-100之间的随机数 [0,100)
                return new Random().nextInt(100);
            }
        });

        System.out.println(f2.get());

    }
}

有返回值的实操代码累加1-100的和:

/*
    使用Callable接口,计算1-100或者1-200之间所有整数的和
 */
public class Demo03Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //1.使用线程池工厂类Executors提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
        ExecutorService ex = Executors.newFixedThreadPool(2);

        //使用Scanner获取一个整数
        System.out.println("请输入一个整数");
        int i = new Scanner(System.in).nextInt();

        //2.调用线程池ExecutorService中的方法submit,传递线程任务,执行线程任务,接收线程任务的返回值
        Future<Integer> f = ex.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                //计算1-i之间的和
                for (int j = 1; j <= i; j++) {
                    sum += j;
                }
                return sum;
            }
        });
        System.out.println(f.get());
    }
}

Lambda表达式的标准格式:

/*
    Lambda表达式的标准格式:
        (参数)->{重写的方法体}
        一些参数,一个箭头,一段代码

        ()->接口中的抽象方法的参数,有就写上,没有空着   (String s) (int a,int b)
        ->传递:把参数传递给{}中的方法体
        {}:重写接口中的抽象方法的方法体

 */
public class Demo02Lambda {
    public static void main(String[] args) {
        //使用匿名内部类的方式,实现多线程程序
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"-->执行了线程任务");
            }
        }).start();

        //使用lambda表达式实现多线程程序
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"-->执行了线程任务");
        }).start();

        //使用Lambda的省略格式
        new Thread(()->System.out.println(Thread.currentThread().getName()+"-->执行了线程任务")).start();
    }
}

使用Arrays数组工具类中的方法sort对象Person按照年龄进行降序排序

/*
    创建一个数组,类型使用Person
    存储Person对象
    使用Arrays数组工具类中的方法sort对象Person按照年龄进行降序排序

    java.util.Arrays:
        static <T> void sort(T[] a, Comparator<? super T> c)
          根据指定比较器产生的顺序对指定对象数组进行排序。
 */
public class Demo01Lambda {
    public static void main(String[] args) {
        //创建一个数组,类型使用Person.存储Person对象
        Person[] arr = {
                new Person("迪丽热巴",18),
                new Person("古力娜扎",17),
                new Person("马尔扎哈",30),
        };
        //使用Arrays数组工具类中的方法sort对象Person按照年龄进行降序排序
        Arrays.sort(arr, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                // o1-o2升序,o2-o1降序
                return o2.getAge()-o1.getAge();
            }

        });

        //使用Lambda表达式,简化匿名Comparator内部类
        Arrays.sort(arr,(Person o1, Person o2)->{
            return o2.getAge()-o1.getAge();
        });

        //使用Lambda表达式的简化格式
        Arrays.sort(arr,(o1,o2)->o2.getAge()-o1.getAge());

        //遍历数组
        for (Person p : arr) {
            System.out.println(p);
        }
    }
}

Lambda表达式的使用前提:

/*
    Lambda表达式的使用前提:
        1.必须用来简化接口重写的匿名内部类
        2.接口中只能有一个抽象方法(函数式接口),才能使用Lambda表达式简化
        Lambda表达式是可推导,可能省略
        接口中只有一个抽象方法,所以Lambda重写的就是这个抽象方法
    Lambda表达式的省略格式:
        ():()中的参数,数据类型是可以省略的   (int a,int b)-->(a,b)
            Lambda重写的就是这个抽象方法,抽象方法的参数的类型是固定的,所以可以推导出来,就可以省略了
        ():()中如果只有一个参数,那么类型和()都可以省略 (int a)-->a
            注意:如果没有参数()不能省略
        {}:{}中的方法体,无论是否有返回值,如果只有一行代码
           那么{},return,;都可以省略,但是必须3个一起省略
 */
public class Demo01Lambda {
    public static void main(String[] args) {
        //创建集合对象,必须指定集合的泛型,在JDK1.7之前集合前后的泛型都必须写出来
        ArrayList<String> list01 = new ArrayList<String>();

        //在JDK1.7之后,后边的泛型可以根据前边的泛型推导出来,所以可以省略
        ArrayList<String> list02 = new ArrayList<>();
    }
}

函数式接口:

能够使用Supplier函数式接口
Supplier接口用于生产数据,接口指定什么类型,就会调用get方法生产什么类型的数据返回

能够使用Consumer函数式接口
Consumer接口是一个消费型接口,指定接口是什么泛型,accept方法就会消费什么类型的数据
传递什么指定的类型的数据,想怎么消费数据都可以,看心情

能够使用Function函数式接口
Function<T,R>用来进行类型转换,可以根据一个类型的数据得到另一个类型的数据
常用的方法:
R apply(T t),根据类型T的参数获取类型R的结果。
使用的场景例如:将String类型转换为Integer类型。

能够使用Predicate函数式接口
Predicate对某种类型的数据进行判断,从而得到一个boolean值结果。
常用的方法:
boolean test(T t):用来对指定泛型类型的数据进行判断
符合条件,方法返回true;不符合条件,就返回false

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值