• 多线程常用基础


    一:进程与线程的描述:

           进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1~n个线程。(进程是资源分配的最小单位)

      线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)

    二:实现多线程的常用方式:

        1.继承Thread类
        2.实现Runnable接口

    三:对于直接继承Thread

            看例子:

    public class Zy  extends Thread{
    
        private  String name;
        public Zy(){}
    
        public  Zy(String name){
            this.name = name;
        }
        public  void run(){
    
            for (int i=0;i<5;i++){
                System.out.println(name+"执行     "+i+"次");
            }
        }
    public class Test {
    
        public static void main(String[] args) {
    
                 Zy z1 = new Zy("z1");
                 Zy z2 = new Zy("z2");
                 z1.run();    //这里不能直接调用test()方法
                 z2.run();    //这里的run方法手是重写Thread的run方法,然后调用才能start()
        }
    
    }

           实现效果:

    z1执行     0次
    z1执行     1次
    z1执行     2次
    z1执行     3次
    z1执行     4次
    z2执行     0次
    z2执行     1次
    z2执行     2次
    z2执行     3次
    z2执行     4次

    这种方法执行顺序是不对的,应该调用start()方法

     public static void main(String[] args) {
    
                 Zy z1 = new Zy("z1");
                 Zy z2 = new Zy("z2");
                 z1.start();
                 z2.start();
    
        }

    注:只有重写了Thread里的run()方法start()方法才能实现效果。

    实现效果

    z2执行     0次
    z1执行     0次
    z2执行     1次
    z1执行     1次
    z2执行     2次
    z1执行     2次
    z2执行     3次
    z1执行     3次
    z2执行     4次
    z1执行     4次

    因为需要用到CPU的资源,所以每次的运行结果基本是都不一样的。

    至于为什么不能直接调用run()方法,应该是线程的运行需要本地操作系统的支持。

    此处调用的是start0()。并且这个这个方法用了native关键字,次关键字表示调用本地操作系统的函数。因为多线程的实现需要本地操作系统的支持。

    通过实现Runnable接口

    public class Zy implements Runnable{
    
        private  String name;
        public Zy(){}
    
        public  Zy(String name){
            this.name = name;
        }
        public  void run(){
    
            for (int i=0;i<5;i++){
                System.out.println(name+"执行     "+i+"次");
            }
        }
    
    
    }
    

      

      public static void main(String[] args) {
    
                 Zy z1 = new Zy("z1");
                 Thread t1 = new Thread(z1);
                 Zy z2 = new Zy("z2");
                 Thread t2 = new Thread(z2);
                 t1.start();
                 t2.start();
    
        }
    

     //执行结果

    第一次:
    z1执行 0次 z1执行 1次 z2执行 0次 z2执行 1次 z2执行 2次 z2执行 3次 z2执行 4次 z1执行 2次 z1执行 3次 z1执行 4次
    第二次:

    z1执行 0次
    z1执行 1次
    z1执行 2次
    z1执行 3次
    z1执行 4次
    z2执行 0次
    z2执行 1次
    z2执行 2次
    z2执行 3次
    z2执行 4次

     //每次执行结果大致都不一样,如果大致一样可能你的操作系统比较稳定,呵呵

     关于选择继承Thread还是实现Runnable接口?

       其实查看源码发现

        

    public
    class Thread implements Runnable {
        /* Make sure registerNatives is the first thing <clinit> does. */
        private static native void registerNatives();
        static {
            registerNatives();
        }
    

     Thread也是实现了Runnable接口,Thread和Runnable都实现了run方法,这种操作模式其实就是代理模式。关于代理模式 我不太清楚  不过这:http://www.cnblogs.com/rollenholt/archive/2011/08/18/2144847.html

    ThreadRunnable的区别:

    1.如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

      这里实现继承Thread

    public class Zy extends Thread{
    private  int count=5;
    public void run(){

    for (int i = 0; i < 6; i++) {
    if (count > 0) {
    System.out.println("数量= " + count--);
    }
    }

    }
    public static void main(String[] args) {

    Zy z1 = new Zy();
    Thread t1 = new Thread(z1);
    Zy z2 = new Zy();
    Thread t2 = new Thread(z2);
    t1.start();
    t2.start();

    }
     }

    执行效果

    数量= 5
    数量= 4
    数量= 3
    数量= 2
    数量= 1
    数量= 5
    数量= 4
    数量= 3
    数量= 2
    数量= 1

     这种说明资源并不能共享,换成runnable接口

    public class Zy implements Runnable{
    
        private  int count=5;
        public  void run(){
    
            for (int i = 0; i < 6; i++) {
                if (count > 0) {
                    System.out.println("数量= " + count--);
                }
            }
    
        }
        public static void main(String[] args) {
    
             Zy zy = new Zy();
             new Thread(zy,"1号窗口").start();
             new Thread(zy, "2号窗口").start();
             new Thread(zy, "3号窗口").start();
    
        }
    
    
    }
    

     (可能实现的)实现效果

    数量= 5
    数量= 2
    数量= 1
    数量= 3
    数量= 4
    

      

    总结一下吧:

    实现Runnable接口比继承Thread类所具有的优势:

    1):适合多个相同的程序代码的线程去处理同一个资源

    2):可以避免java中的单继承的限制

    3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。

    另外:main方法其实也是一个线程。在java中所有的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。

    在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM事实上就是在操作系统中启动了一个进程。

     四:判断线程是否启动

    public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName());
            }
        }
    
        public static void main(String[] args) {
            Zy zy = new Zy();
            Thread demo = new Thread(zy);
            System.out.println("线程启动之前---》" + demo.isAlive());
            demo.start();
            System.out.println("线程启动之后---》" + demo.isAlive());
        }
    

      执行结果

    线程启动之前---》false
    线程启动之后---》true
    Thread-0
    Thread-0
    Thread-0
    

      

    主线程也有可能在子线程结束之前结束。并且子线程不受影响,不会因为主线程的结束而结束。

    五:线程的强制执行join:

        

    public class Zy implements Runnable{
    
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName());
            }
        }
    
        public static void main(String[] args) {
            Zy zy = new Zy();
            Thread demo = new Thread(zy,"线程");
            demo.start();
            for(int i=0;i<10;++i){
                if(i>5){
                    try{
                        demo.join();  //强制执行demo
                    }catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("main 线程执行-->"+i);
            }
        }
    
    
    }
    

      

     执行结果

    main 线程执行-->0
    main 线程执行-->1
    main 线程执行-->2
    main 线程执行-->3
    main 线程执行-->4
    main 线程执行-->5
    线程
    线程                    //就是在主线程没有结束之前强行执行
    线程
    main 线程执行-->6
    main 线程执行-->7
    main 线程执行-->8
    main 线程执行-->9
    

     六:线程休眠sleep:

    public class Zy implements Runnable{
    
        public void run() {
            for (int i = 0; i < 3; i++) {
                try {
                    Thread.sleep(2000);    //2000指2秒
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + i);
            }
        }
    
        public static void main(String[] args) {
            Zy zy = new Zy();
            Thread demo = new Thread(zy, "线程");
            demo.start();
        }
    
    
    }

     执行结果

    线程0 
    线程1
    线程2   //没两秒打印一个

        七,线程中断interrupt()

        

    public class Zy implements Runnable{
    
        public void run() {
            System.out.println("执行run方法");
            try {
                Thread.sleep(10000);
                System.out.println("线程完成休眠");
            } catch (Exception e) {
                System.out.println("休眠被打断");
                return;  //返回到程序的调用处
            }
            System.out.println("线程正常终止");
        }
    
        public static void main(String[] args) {
            Zy zy = new Zy();
            Thread demo = new Thread(zy, "线程");
            demo.start();
            try{
                Thread.sleep(2000);  
            }catch (Exception e) {
                e.printStackTrace();
            }
            demo.interrupt(); //2s后中断线程
        }
    }

     执行结果

    执行run方法
    
    -----间隔两秒后
    
    休眠被打断

    java程序中,只要前台有一个线程在运行,整个java程序进程不会消失,所以此时可以设置一个后台线程,这样即使java进程消失了,此后台线程依然能够继续运行。主线程中断 不会打断子线程

    public class Zy implements Runnable{
    
        public void run() {
            while (true) {
                System.out.println(Thread.currentThread().getName() + "在运行");
            }
        }
    
        public static void main(String[] args) {
            Zy zy = new Zy();
            Thread demo = new Thread(zy, "线程");
            demo.setDaemon(true);
            demo.start();
        }
    
    
    }

    八:线程的优先级

          查看Thread源码发现

     /**
         * The minimum priority that a thread can have.  //最低优先级
         */
        public final static int MIN_PRIORITY = 1;
    
       /**
         * The default priority that is assigned to a thread.
         */
        public final static int NORM_PRIORITY = 5;
    
        /**
         * The maximum priority that a thread can have.
         */
        public final static int MAX_PRIORITY = 10;
        //MIN_PRIORITY值越小优先级越低
    public class Zy implements Runnable{
    
        public void run() {
            for(int i=0;i<5;++i){
                System.out.println(Thread.currentThread().getName()+"运行"+i);
            }
        }
    
        public static void main(String[] args) {
            Thread h1=new Thread(new Zy(),"A");
            Thread h2=new Thread(new Zy(),"B");
            Thread h3=new Thread(new Zy(),"C");
            h1.setPriority(8);
            h2.setPriority(2);
            h3.setPriority(6);
            h1.start();
            h2.start();
            h3.start();
        }
    
    
    }

    打印效果

    A运行0
    A运行1
    A运行2
    A运行3
    A运行4
    C运行0
    C运行1
    C运行2
    C运行3
    C运行4
    B运行0
    B运行1
    B运行2
    B运行3
    B运行4

    但是请读者不要误以为优先级越高就先执行。谁先执行还是取决于谁先去的CPU的资源、不知道是不是

    NORM_PRIORITY值越高,谁就可以先拿到cpu资源,我执行了n次结果都是一样的?

    另外,主线程的优先级是5.

    九:线程的礼让。

        在线程操作中,也可以使用yield()方法,将一个线程的操作暂时交给其他线程执行。

    public class Zy implements Runnable{
    
        public void run() {
            for(int i=0;i<5;++i) {
                System.out.println(Thread.currentThread().getName() + "运行" + i);
                if (i == 3) {
                    System.out.println("线程的礼让");
                    Thread.currentThread().yield();
                }
            }
        }
    
        public static void main(String[] args) {
            Thread h1=new Thread(new Zy(),"A");
            Thread h2=new Thread(new Zy(),"B");
            h1.start();
            h2.start();
        }
    
    
    }

    执行效果

    A运行0
    A运行1
    A运行2
    A运行3
    线程的礼让
    B运行0
    B运行1
    B运行2
    B运行3
    线程的礼让
    A运行4
    B运行4

    十:同步和死锁。

          

    public class Zy implements Runnable{
        private int count=5;
        public void run() {
            for(int i=0;i<10;++i){
                if(count>0){
                    try{
                        Thread.sleep(1000);  //休息一秒
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    System.out.println(count--);
               }
            }
        }
    
        public static void main(String[] args) {
           Zy zy = new Zy();
            Thread h1=new Thread(zy);
            Thread h2=new Thread(zy);
            Thread h3=new Thread(zy);
            h1.start();
            h2.start();
            h3.start();
        }
    
    
    }

    执行效果

    5
    3
    4
    2
    2
    2
    1
    0
    -1

    这里应该是线程之间出现了纷争,

    如果想解决这种问题,就需要使用同步。所谓同步就是在统一时间段中只有有一个线程运行,

    其他的线程必须等到这个线程结束之后才能继续执行。

    【同步代码块】:

    语法格式:

    synchronized(同步对象){

     //需要同步的代码

    }

    public class Zy implements Runnable{
        private int count=5;
        public void run() {
            for(int i=0;i<10;++i){
                synchronized (this) {
                    if (count > 0) {
                        try {
                            Thread.sleep(1000);  //休息一秒
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(count--);
                    }
                }
            }
        }
    
        public static void main(String[] args) {
           Zy zy = new Zy();
            Thread h1=new Thread(zy);
            Thread h2=new Thread(zy);
            Thread h3=new Thread(zy);
            h1.start();
            h2.start();
            h3.start();
        }
    
    
    }

    执行结果

    5
    4
    3
    2
    1
    //没秒输出一个

    【同步方法】

    也可以采用同步方法。

    语法格式为synchronized 方法返回类型方法名(参数列表){

        // 其他代码

    }

    public class Zy implements Runnable{
        private int count=5;
        public void run() {
            for (int i = 0; i < 10; ++i) {
                sale();
            }
        }
        public synchronized void sale() {
            if (count > 0) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(count--);
            }
        }
    
        public static void main(String[] args) {
           Zy zy = new Zy();
            Thread h1=new Thread(zy);
            Thread h2=new Thread(zy);
            Thread h3=new Thread(zy);
            h1.start();
            h2.start();
            h3.start();
        }
    
    
    }

    //输出结果一样,不信 你可以试一下

    死锁

    当多个线程共享一个资源的时候需要进行同步,但是过多的同步可能导致死锁。

    【生产者和消费者问题】

    /*
    *公共信息类
    */
    public class Info {
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        private String name = "Rollen";
        private int age = 20;
    }
    
    /**
    *消费者类
    */
    public class Consumer implements Runnable {
        private Info info=null;
        public Consumer(Info info){
            this.info=info;
        }
    
        public void run(){
            for(int i=0;i<25;++i){
                try{
                    Thread.sleep(100);
                }catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println(this.info.getName()+"<---->"+this.info.getAge());
            }
        }
    }
    /**
    *生产类
    */
    public class Producer implements Runnable {
        private Info info=null;
        Producer(Info info){
            this.info=info;
        }
    
        public void run(){
            boolean flag=false;
            for(int i=0;i<25;++i){
                if(flag){
                    this.info.setName("Rollen");
                    try{
                        Thread.sleep(100);
                    }catch (Exception e) {
                        e.printStackTrace();
                    }
                    this.info.setAge(20);
                    flag=false;
                }else{
                    this.info.setName("chunGe");
                    try{
                        Thread.sleep(100);
                    }catch (Exception e) {
                        e.printStackTrace();
                    }
                    this.info.setAge(100);
                    flag=true;
                }
            }
        }
    
    }

    测试类

    public static void main(String[] args) {
    
            Info info = new Info();
            Producer pro = new Producer(info);
            Consumer con = new Consumer(info);
            new Thread(pro).start();
            new Thread(con).start();
    
        }

    测试结果

    Rollen<---->100
    chunGe<---->20
    chunGe<---->100
    Rollen<---->100
    chunGe<---->20
    chunGe<---->20
    Rollen<---->100
    Rollen<---->100
    Rollen<---->100
    chunGe<---->20
    chunGe<---->100
    chunGe<---->20
    chunGe<---->20
    chunGe<---->20
    chunGe<---->100
    Rollen<---->20
    chunGe<---->100
    Rollen<---->20
    chunGe<---->100
    chunGe<---->20
    Rollen<---->100
    chunGe<---->20
    Rollen<---->100
    Rollen<---->20
    chunGe<---->20   //名字和年龄并不对应

    那么如何解决呢?

    1)加入同步

    2)加入等待和唤醒

    首先是加入同步

    class Info {
         
        public String getName() {
            return name;
        }
     
        public void setName(String name) {
            this.name = name;
        }
     
        public int getAge() {
            return age;
        }
     
        public void setAge(int age) {
            this.age = age;
        }
     
        public synchronized void set(String name, int age){
            this.name=name;
            try{
                Thread.sleep(100);
            }catch (Exception e) {
                e.printStackTrace();
            }
            this.age=age;
        }
         
        public synchronized void get(){
            try{
                Thread.sleep(100);
            }catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(this.getName()+"<===>"+this.getAge());
        }
        private String name = "Rollen";
        private int age = 20;
    }
     
    /**
     * 生产者
     * */
    class Producer implements Runnable {
        private Info info = null;
     
        Producer(Info info) {
            this.info = info;
        }
     
        public void run() {
            boolean flag = false;
            for (int i = 0; i < 25; ++i) {
                if (flag) {
                     
                    this.info.set("Rollen", 20);
                    flag = false;
                } else {
                    this.info.set("ChunGe", 100);
                    flag = true;
                }
            }
        }
    }
     
    /**
     * 消费者类
     * */
    class Consumer implements Runnable {
        private Info info = null;
     
        public Consumer(Info info) {
            this.info = info;
        }
     
        public void run() {
            for (int i = 0; i < 25; ++i) {
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                this.info.get();
            }
        }
    }
     
    /**
     * 测试类
     * */
    class hello {
        public static void main(String[] args) {
            Info info = new Info();
            Producer pro = new Producer(info);
            Consumer con = new Consumer(info);
            new Thread(pro).start();
            new Thread(con).start();
        }
    }

    实现效果

    Rollen<===>20
    Rollen<===>20
    ChunGe<===>100
    Rollen<===>20
    ChunGe<===>100
    Rollen<===>20
    ChunGe<===>100
    Rollen<===>20
    ChunGe<===>100
    Rollen<===>20
    ChunGe<===>100
    ChunGe<===>100
    Rollen<===>20
    ChunGe<===>100
    ChunGe<===>100
    Rollen<===>20
    ChunGe<===>100
    ChunGe<===>100
    ChunGe<===>100
    ChunGe<===>100
    ChunGe<===>100
    ChunGe<===>100
    ChunGe<===>100
    ChunGe<===>100
    ChunGe<===>100

    名字和年龄是对应了,但是存在有重复覆盖的问题

    如果想解决这个问题,就需要使用Object类帮忙了、

    ,我们可以使用其中的等待和唤醒操作。

    要完成上面的功能,我们只需要修改Info类饥渴,在其中加上标志位,并且通过判断标志位完成等待和唤醒的操作,代码如下

    public synchronized void set(String name, int age){
            if(!flag){
                try{
                    super.wait();
                }catch (Exception e) {
                    e.printStackTrace();
                }
            }
            this.name=name;
            try{
                Thread.sleep(100);
            }catch (Exception e) {
                e.printStackTrace();
            }
            this.age=age;
            flag=false;
            super.notify();
        }
         
        public synchronized void get(){
            if(flag){
                try{
                    super.wait();
                }catch (Exception e) {
                    e.printStackTrace();
                }
            }
             
            try{
                Thread.sleep(100);
            }catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(this.getName()+"<===>"+this.getAge());
            flag=true;
            super.notify();
        }
        private String name = "Rollen";
        private int age = 20;
        private boolean flag=false;

    结果

    Rollen<===>20
    ChunGe<===>100
    Rollen<===>20
    ChunGe<===>100
    Rollen<===>20
    ChunGe<===>100
    Rollen<===>20
    ChunGe<===>100
    Rollen<===>20
    ChunGe<===>100
    Rollen<===>20
    ChunGe<===>100
    Rollen<===>20
    ChunGe<===>100
    Rollen<===>20
    ChunGe<===>100
    Rollen<===>20
    ChunGe<===>100
    Rollen<===>20
    ChunGe<===>100
    Rollen<===>20
    ChunGe<===>100
    Rollen<===>20
    ChunGe<===>100
    Rollen<===>20

    问题解决

    原文地址:http://www.cnblogs.com/rollenholt/archive/2011/08/28/2156357.html

     
  • 相关阅读:
    USB子系统gadget analyse
    关于嵌入式的学习和职业发展
    编译JNI动态库so
    Linux串口编程
    i2c_get_clientdata和to_i2c_client 的使用获取平台设备
    Kbuild 编译系统分析
    解决QPST 出现的 "服务器正在运行" 的 下载错误.
    Msm 高通平台配置记录之一
    USB Skeleton driver 分析记录
    网站应用数据库相关 过滤类
  • 原文地址:https://www.cnblogs.com/zhangyu0217----/p/7102758.html
Copyright © 2020-2023  润新知