zoukankan      html  css  js  c++  java
  • 22天学习java基础笔记之day12&13

    一、多线程

    1、概述

    在一个进程中有多条执行路径。

    A:进程 正在执行的程序,它代表着应用程序的执行区域。

    B:线程 进程的执行路径.就是进程中一个负责程序执行的控制单元。

    线程总是属于某个进程,进程中的多个线程共享进程的内存。

    2、关于多线程的相关问题

    1. jvm的启动是多线程的还是单线程的,为什么?

    多线程的,它至少启动了两个线程(主线程和垃圾回收线程),垃圾回收机制这个线程不可能是在程序执行完毕后才启动的,否则的话,我们的程序很容易出现内存溢出。

    1. 调用start方法和run方法区别?

    调用start方法后,线程进入就绪状态,此时线程对象仅有执行资格,还没有执行权。当该线程对象抢到了执行权时,方可调用run方法,当run方法执行完毕后,线程死亡,不能复生。

    1. 线程的随机性的导致的原因?

    在同一时刻,CPU只能执行一个程序,这个多线程的程序其实是CPU的高速切换造成的。

    1. 什么时候使用多线程?以及创建线程的目的?

    多线程的引入是为了解决现实生活中的需求的,提高解决问题的效率。比如购买火车票这个动作。当许多对象要对同一有限资源进行操作的时候,我们就要使用多线程。

    1. 线程状态的内容和每一个状态的特点?

    创建线程对象后,并对这个对象进行了一些初始化工作,当调用了start方法后,这个状态就有了执行资格,但是此时还未获得执行权,进入到了就绪状态。当抢到执行权后进入了运行状态,此时该线程既有了执行资格,又有了执行权,当调用了run方法后,此线程进入了死亡状态。当然你也可以调用stop方法令其强制死亡。在运行状态的时候,如果该线程调用了sleep或者wait等方法后,他会进入到阻塞状态,此时,这个对象释放了执行资格和执行权,当他的sleep或者wait时间结束后,亦或者调用了notify(notifyAll)方法,该线程被唤醒,又进入了就绪状态。如此周而复始。

    3、创建线程的方式

    A:继承Thread

    **步骤

                    a:定义一个类继承Thread类;

                    b:重写Thread类中的run()方法,run()方法里边是多线程要运行的代码;

                    c:创建定义的那个类的对象;

                    d:调用start()方法开启线程,执行run()方法里的内容。

    另外可以通过Thread的getName()获取线程的名称。

    public class ThreadDemo extends Thread{

        public void run(){

            for(int x=0;x<10;x++){

                System.out.println("线程"+getName()+"正在运行:"+x);

            }

        }

        public static void main(String[] args) {

            ThreadDemo t1 = new ThreadDemo();

            ThreadDemo t2 = new ThreadDemo();

            t1.start();

            t2.start();

        }

    }

            **线程的生命周期  创建-----阻塞------运行------死亡

    B:实现Runnable接口

            **步骤    

                    a:定义一个类实现Runnable接口;

                    b:重写Runnable接口中的run()方法,run()方法里是多线程要运行的代码;

                    c:创建定义的那个类的对象,并将其作为参数放置于Thread类的对象里。线程的     任务都封装在Runnable接口子类对象的run方法中。所以要在线程对象创建时就必须 明确要运行的任务

                    d:调用start()方法启动线程,执行run()方法里的内容。

    public class RunnableDemo implements Runnable {

        public void run() {

            for(int x = 0;x<10;x++){

                System.out.println(Thread.currentThread().getName()+" "+x);

            }

        }

        public static void main(String[] args) {

            RunnableDemo rd = new RunnableDemo();

            Thread t1 = new Thread(rd);

            Thread t2 = new Thread(rd);

            t1.start();

            t2.start();

        }

    }

    总结:两种方法的比较:实现Runnable接口,将线程的任务从线程的子类中分离出来的,进行了单独的封装,按照面向对象的思想将任务的封装成对象,避免了java中单继承的局限性。(创建线程第二种方式较好)static变量的生命周期过长。

    1. 对创建线程的第二种方式的设计的理解?

    创建多线程的第二种方式是实现Runnable接口,并实现run()方法。由于第一种方式有资源不能共享的缺点,它需要创建很多的线程对象,而这些线程对象对同一资源又是独有的,如果设定为共享资源(设为静态),必将消耗太多内存资源,静态变量的生命周期过长。此外,如果一个线程对象类继承了其他类,此时他无法继承Thread类,也就不能使用第一种方式来创建线程。

    4、多线程的安全问题

    • 产生的原因

            A:线程访问的延迟

            B:线程的随机性

    • 线程安全问题表现?原因?解决思想?解决具体的体现?

    当一个线程对象在执行run方法的某一操作时,其他线程对象也进来了,并发的访问了临界资源,破环了原子操作,造成了数据的不一致。

    多线程访问的延迟和线程的随机性产生了线程的安全问题。

    当某一线程对象进入了run方法后,如果能做一个标记,说我已经在里面了,其他的哥们(线程对象)你就等着吧,等我操作完了,出来去掉标记你再进去吧。这样一来,原子操作就不会遭到破坏。

    具体体现就是给那个原子操作加锁,使整个操作同步,不让其他线程对象破环,保证数据的一致性。

    5、同步解决线程安全问题

    A:同步代码块

            同步代码块中的锁可以是任意对象,但是要在成员范围内定义.

            在局部的话,会导致锁发生变化,因为你每次执行方法,都会重新创建一个对象.         

                    **同步的前提

                            ***至少要有两个线程

                            ***同一个锁

                    **同步的好处 提高了安全性

                    **同步的弊端 效率较低

    安全性和效率是你们一直要考虑的问题,而且很多时候,他们是对立的关系。

    B:同步函数

    1. 同步函数的使用

                    用synchronized关键字修饰方法即可。

                            public synchronized void show(){

                                    //需要同步的代码块

                            }

    1. 同步函数使用的锁

                    同步函数使用是this对象锁,静态同步函数的锁是(类名.class)

    //采用双重判断完成,同步函数效率低,所以采用同步代码块完成单例的延迟加载

    public class Singleton {

        private Singleton(){}

        private static Singleton s = null;

        public static Singleton getInstance(){

            if(s==null){

                synchronized(Singleton.class){

                    if(s==null){

                        s = new Singleton();

                    }

                }

            }

            return s;

        }

    }

    1. 同步的好处,弊端,前提?

    它能解决多线程的安全问题,但是效率却下降了许多。前提有二:首先必须得有两个或者两个以上的线程,其次就是这些线程用的是同一把锁。

    6、死锁

            每个线程都不会释放自己拥有的锁标记,却阻塞在另外的线程所拥有的锁标记的对象锁池中,就会造成死锁现象。

    1. 产生原因

            假如有A和B两个锁,在A锁中要使用B锁,在B锁中要是A锁,而他们都不想让,最终导致了死锁.

                    因为系统资源不足。

                    进程运行推进的顺序不合适。

                    资源分配不当等。

            如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则

    就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。

    public class DeadLockDemo implements Runnable {

        public boolean flag = false;

        public void run() {

            if (!flag) {

                while (true) {

                    synchronized (this) {

                        synchronized (DeadLockDemo.class) {

                            System.out.println("true");

                        }

                    }

                }

            } else {

                while (true) {

                    synchronized (DeadLockDemo.class) {

                        synchronized (this) {

                            System.out.println("false");

                        }

                    }

                }

            }

        }

        public static void main(String[] args) {

            DeadLockDemo dld = new DeadLockDemo();

            Thread t1 = new Thread(dld);

            Thread t2 = new Thread(dld);

            t1.start();

            try{

                Thread.sleep(10);

            }catch(InterruptedException e){

               

            }

            dld.flag=true;

            t2.start();

        }

    }

    1. 产生死锁的四个必要条件

                    互斥条件:一个资源每次只能被一个进程使用。

                    请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

                    不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。

                    循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

    只要上述条件之一不满足,就不会发生死锁。

    1. 如何解决?

            不同时在A锁中用B锁,B锁中用A锁.

            理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和

    解除死锁。所以,在系统设计、进程调度等方面注意如何不让这四个必要条件成立,如何确

    定资源的合理分配算法,避免进程永久占据系统资源。此外,也要防止进程在处于等待状态

    的情况下占用资源。因此,对资源的分配要给予合理的规划。

    7、线程状态图解

    创建:使用start()开启线程。

    运行:具备着执行资格,具备着执行权

    冻结:释放执行权,同时释放执行资格      

    从运行到冻结的方式:

    sleep(time),sleep(time)时间到,进入临时阻塞状态(具备着执行资格,但是不具备执行权,正在等待执行权)

    wait()线程等待,notify()线程唤醒,进入临时阻塞状态。

    消亡:

    从运行到消亡的方式:

    stop()中止线程;

    run()方法结束,线程的任务结束。

    二、线程间的通信

    1、概述

    以前呢,是同一个操作的多个线程来执行一个资源。

    现在呢,需求变了,不同的操作,多个线程来完成对某个资源操作。

    举例:        池水 放水的时候,加水。

                    一堆煤 一辆车把煤拉走,一辆车把煤拉到。

    2、应用(等待唤醒机制)

    需求:一个线程给学生赋值,另一个线程输出学生的内容。

    class Student{

                    String name;

                    int age;

            }

            class Input implements Runnable{

                    private Student s;

                    Input(Student s){

                            this.s = s;

                    }

                    public void run(){

                            int x = 0;

                            while(true){

                                    if(x==0){//对共享数据的操作分了多条语句来做的

                                            s.name="aa";

                                            s.age = 10;

                                    }else{

                                            s.name="bb";

                                            s.age = 20;

                                    }             

                            }             

                    }

            }

            class Output implements Runnable{

                    private Student s;

                    Output(Student s){

                            this.s = s;

                    }

                    public void run(){

                            while(true){

                                    System.out.println(s.name+"  "+s.age);

                            }

                    }

            }

            class StudentTest{

                    public static void main(String[] args){

                            Studnet s = new Student();

                            Input in = new Input(s);

                            Output out = new Output(s);

                            Thread t1 = new Thread(in);

                            Thread t2 = new Thread(out);

                            t1.start();

                            t2.start();

                    }

            }

    到此我们发现程序出问题了,就是说aa有可能拿的是bb的年龄,bb有可能拿的是aa的年龄

    为什么会出这个问题呢?

    线程的随机性.

    线程安全问题的产生:

            1:共享数据

            2:对共享数据的操作分了多条语句来做的。

    安全问题产生后,我们怎么解决的?

            1:同步代码块

            2:同步函数

    经过比较,我们发现同步代码块比较合适,而且在这个过程中,我们一直在找同步代码块中的对象用谁比较合适Object--this--Student s------>资源唯一,可以作为锁!

            class Input implements Runnable{

                    private Student s;

    Input(Student s){

                            this.s = s;

                    }

                    public void run(){

                            int x = 0;

                            while(true){    

                                    synchronized(s){

                                            if(x==0){

                                                    s.name="aa";

                                                    s.age = 10;

                                            }else{

                                                    s.name="bb";

                                                    s.age = 20;

                                            }

                                    }

                            }             

                    }

            }

            class Output implements Runnable{

                    private Student s;

                    Output(Student s){

                            this.s = s;

                    }

                    public void run(){

                            while(true){

                                    synchronized(s){

                                            System.out.println(s.name+"  "+s.age);

                                    }

                            }

                    }

            }

    这个时候呢,我们已经完成了线程间的通信,但是效果不是很理想,我们想的是,有值我就输出,没有值,我就先产生一个值。找java,问他有没有提供这样的一个操作,发现它提供了

    wait() 让线程等待

            notify() 唤醒线程

            notifyAll() 唤醒线程池中的所有线程

    这个时候,我们是如何改进代码的呢?继续

            class Student{

                    private String name;

                    private int age;

                    private boolean flag ;

     

            }

            class Input implements Runnable{

                    private Student s;

                    Input(Student s){

                            this.s = s;

                    }

                    public void run(){

                            int x = 0;

                            while(true){    

                                    synchronized(s){

                                            if(s.flag){

                                                    try{s.wait();}catch (InterruptedException ie){}

                                            }

                                            if(x==0){

                                                    s.name="aa";

                                                    s.age = 10;

                                            }else{

                                                    s.name="bb";

                                                    s.age = 20;

                                            }

                                            s.flag = true;

                                            s.notify();

                                            x = (x+1)%2;

                                    }

                            }           

                    }

            }

            class Output implements Runnable{

                    private Student s;

    Output(Student s){

                            this.s = s;

                    }

                    public void run(){

                            while(true){

                                    synchronized(s){

                                            if(!s.flag){

                                                    try{s.wait();}catch (InterruptedException ie){}

                                            }

                                            System.out.println(s.name+"  "+s.age);

                                            s.flag = false;

                                            s.notify();

                                    }

                            }

                    }

            }     

    wait(),notify(),notifyAll()

    通过抓人游戏生动的描述了一下。

    3、几个方法的使用

    1. 为什么wait(),notify(),notifyAll()都定义在Object类中?

            A:这些方法存在于同步中。

            B:使用这些方法时必须要标识所属的同步的锁。

            C:锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。

    1. wait()sleep()的区别

            A:对时间指定而言

                    wait():可以不指定时间。

                    sleep():必须指定时间。

            B:对执行权和锁而言

                    wait():释放cpu执行权(资格也没了),释放锁。存储于线程池

                    sleep():释放cpu执行权,不释放锁(会自动醒)。

    1. 停止线程

            A:通过控制循环

            B:interrupt()方法

                    **stop已过时,被interrupt取代

    1. 守护线程 后台线程

            你只要把一个线程设置为守护线程,那么主方法线程结束,不管什么情况,守护线程就结束.

            举例:坦克大战

            A:setDaemon(boolean flag)

    1. join:加入线程,把执行权抢夺,自己执行完毕,其他的线程才可能有机会执行.
    2. toString():线程名称,优先级,线程组(是由多个线程组成的,默认的线程组是main)
    3. yield():让本线程暂停执行,把执行权给其他线程.
    4. setPriority(int num):设置线程优先级

            getPrinrity():获取线程优先级

               线程的级别:1 - 10

               默认级别为5.   

    4、生产者与消费者

    解决了多个生产者与多个消费者的问题

    public class ThreadTest{

        public static void main(String[] args){

            Resource r = new Resource();

            Shengchan sc = new Shengchan(r);

            Xiaofei xf = new Xiaofei(r);

            Thread t1 = new Thread(sc);

            Thread t2 = new Thread(sc);

            Thread t3 = new Thread(sc);

            Thread t4 = new Thread(xf);

            Thread t5 = new Thread(xf);

            t1.start();

            t2.start();

            t3.start();

            t4.start();

            t5.start();

        }

    }

    class Resource{

        private String name;

        private int count = 1;

        private boolean flag = false;

        public synchronized void set(String name){

            while (flag)

                // 这里写while循环是为了当线程每次醒后再判断一次标记。

                try {

                    this.wait();

                } catch (InterruptedException e){

                }

            this.name = name + count;

            count++;

            System.out.println(Thread.currentThread().getName() + "...生产者...."+ this.name);

            flag = true;

            notifyAll();

        }

        public synchronized void out(){

            while (!flag)

                try {

                    this.wait();

                } catch (InterruptedException e){

                }

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

                    + ".........消费者........." + this.name);

            flag = false;

            notifyAll();

        }

    }

    class Shengchan implements Runnable{

        Resource r;

        Shengchan(Resource r){

            this.r = r;

        }

        public void run(){

            while (true)

                r.set("烤鸭");

        }

    }

    class Xiaofei implements Runnable{

        Resource r;

        Xiaofei(Resource r){

            this.r = r;

        }

        public void run(){

            while (true)

                r.out();

        }

    }

    5Lock&Condition接口

    JDK1.5以后将同步和锁封装成了对象。并将操作锁的隐式方式定义到了对象中,将隐式动作编程了显示动作。对多线程中的内部细节进行了升级改良。

    它的出现替代了同步代码块或者同步函数。将同步的隐式锁操作编程了显示锁操作。 同事更为灵活。可以一个锁上加上多组监视器。

    在java.util.concurrent.locks包中提供了一个Lock接口。Lock接口中提供了lock()获取锁,unlock()释放锁(通常需要定义finally代码块中)的操作。Lock接口更符合面向对象的思想,将锁这种事物封装成了对象。

    public void run(){

        synchronized(obj){//获取锁。

             code...

             //释放锁。

        }

    }

    但是对于释放和获取锁的操作,都是隐式的。

    JDK1.5后,就有了新的方法,将锁封装成了对象。因为释放锁和获取锁动作,锁自己最清楚。

    锁对象的类型就是Lock接口。并提供了,显示的对锁的获取和释放的操作方法。

    Lock lock;

    public void run(){

       try{

           lock.lock();//获取锁。

           code...throw ...

       }finally{

           lock.unlock();//释放锁.

       }

    }
        Lock
    接口替代了synchronized

        Condition替代了Object类中监视器方法 wait notify  notifyAll。

        将监视器方法单独封装成了Condition对象。而且一个锁上可以组合多组监视器对象。

    实现了多生产者多消费者时,本方只唤醒对方中一个的操作,提高效率。

    await()睡眠;signal(),signalAll()唤醒;将这些监视器方法单独进行封装,变成了Condition监视器对象。可以任意锁进行组合。

    使用为一般是生产者是被消费者唤醒,消费者是被生产者唤醒。

    6Scanner (扩展)

    接受从键盘输入的数据

    (1)这是java中提供的类,在util包中

    (2)用法基本格式:

       Scanner sc = new Scanner(System.in);

       通过sc对象就可以取得值了。

       输入每个数据换行

       int--int OK

       String--String OK

       int--String No

       String--int OK 

       输入数据在一行上.就没有问题

    Scanner类的使用 猜数字小游戏

    import java.util.Scanner;

    import java.util.Random;

    class CaiShuTest {

    public static void main(String[] args) {

       System.out.println("请输入一个1~100之间的整数!");

       Random r = new Random();

       int b = r.nextInt(100);

       guessNum(b);

    }

    public static void guessNum(int b){

       Scanner sc = new Scanner(System.in);

       while (true){

          int a= sc.nextInt();

          if (a<b){

             System.out.println("你猜的数太小了");

             System.out.println("请继续输入!");

          }else if (a>b){

             System.out.println("你猜的数太大了");

             System.out.println("请继续输入!");

          }else{

             System.out.println("恭喜您猜对了");

             break;

          }

       }

    }

    }

  • 相关阅读:
    xe5 android tts(Text To Speech) [转]
    xe5 android sample 中的 SimpleList 是怎样绑定的 [转]
    xe5 android 控制蓝牙[转]
    xe5 android 调用照相机获取拍的照片[转]
    XE5 Android 开发数据访问手机端[转]
    XE5 Android 开发实现手机打电话和发短信 [转]
    让VCL的皮肤用在手机程序里 让安桌程序不山寨[转]
    XE5 Android 开发数据访问server端[转]
    XE5 Android 开发实现手机打电话和发短信[转]
    Delphi XE5的Android开发平台搭建[转]
  • 原文地址:https://www.cnblogs.com/aohongzhu/p/12938752.html
Copyright © 2011-2022 走看看