美文网首页
Java多线程笔记

Java多线程笔记

作者: ZGYSYY | 来源:发表于2018-05-11 23:40 被阅读0次

    Java多线程笔记

    什么是线程?

    线程是程序执行的一条路径,一个进程中可以包含多条线程。一个应用程序可以理解为一个进程。多线程并发执行可以提高程序的效率,可以同时完成多项任务。

    多线程并行和并发的区别

    1. 并行
      并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)
    2. 并发
      并发是指两个任务都请求运行,而处理器只能接受一个任务,就把这两个任务安排轮流进行,由于CPU运算速度较快,使人感觉两个任务都在运行。

    注意:以上说的两个任务只是举例,可以是多个任务。

    Java程序运行原理和JVM启动是多线程的吗?

    1. Java程序运行原理:Java命令会启动Java虚拟机(JVM),等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个“主线程”,然后主线程去调用某个类的main方法。一个应用程序只有一个主线程。

    2. JVM是多线程的吗?
      JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。以下代码用来证明JVM是多线成的。
      package demo01;

      public class Demo01 {
      
       public static void main(String[] args) {
           
           System.out.println("AAA");
           System.out.println("BBB");
           
           //打印当前线程名称
           System.out.println("当前线程为:"+Thread.currentThread().getName());
           
           //模拟java的垃圾回收
           for(int i=0; i<2; i++) {
               new Student();
               System.gc();
           }
       }
      }
      
      class Student{
      
       @Override
       protected void finalize() throws Throwable {
           
           //打印当前线程名称
           System.out.println("当前线程名称:"+Thread.currentThread().getName());
       }
       
       
      }
      

    线程的实现方式

    1. 继承Thread类
      使用步骤:定义类继承Thread;重写run方法,把新线程要做的事情写在run方法中;创建线程对象;启动线程。
      以下是式例代码:
      package demo01;

      public class Demo02 {
      
       public static void main(String[] args) {
           /**
            * 本例用来创建多个线程
            */
           
           //创建线程对象
           MyThread thread = new MyThread();
           //启动线程,注意:是调用start方法而run方法,如果直接调用run方法,其实就是在主线程中运行run方法,并没有开启线程。
           thread.start();
           
           //循环创建多个线程,用来证明线程的执行,是抢占资源来执行的,并非是有序的。
           for(int i=0; i<10; i++) {
               thread = new MyThread();
               thread.start();
           }
       }
      }
      
      class MyThread extends Thread{
       
       @Override
       public void run() {
           
           System.out.println("执行线程任务----"+Thread.currentThread().getName());
       }
      }
      
    2. 实现Runnable接口
      使用步骤:实现Runnable接口;实现run方法,把新线程要做的事情写在run方法中;创建自定义的实现了Runnable的子类对象;创建Thread对象,将自定义的实现Runnable的子类对象传入Thread的构造方法;启动线程。
      以下是式例代码:
      package demo01;

      public class Demo03 {
      
       public static void main(String[] args) {
           
           //使用线程步骤:
           //创建myTask对象
           MyTask task = new MyTask();
           //使用Thread的构造方法创建Thread对象
           Thread thread = new Thread(task);
           //启动线程
           thread.start();
           
           //循环开启多个线程
           for(int i=0; i<10; i++) {
               thread = new Thread(task);
               thread.start();
           }
       }
      }
      
      class MyTask implements Runnable{
      
       @Override
       public void run() {
           //需要在线程中执行的任务
           System.out.println("所要执行的任务----"+Thread.currentThread().getName());
       }
       
      }
      

    两种方式的区别:

    1. 继承Thread
      好处:可以直接是以哦那个Thread类中的方法,代码简单。
      弊端:如果有了父类,就不能用这种方法,因为Java不支持多继承。
    2. 实现Runnable接口
      好处:即使自己定义的线程类有了父类也没有关系,因为有了父类也能实现接口,代码更灵活。
      弊端:不能直接使用Thread类中的方法,需要先获取到线程对象后,才能得到Thread的方法,代码复杂。

    使用匿名内部类实现多线程,以下是式例代码

    package demo01;
    
    public class Demo04 {
    
        public static void main(String[] args) {
            //使用匿名内部类实现线程
            
            Thread t1 = new Thread() {
                @Override
                public void run() {
                    System.out.println("执行线程任务---------"+this.getName());
                }
            };
            
            t1.start();
            
            Thread t2 = new Thread(new Runnable() {
                
                @Override
                public void run() {
                    System.out.println("执行线程任务---------"+Thread.currentThread().getName());
                }
            });
            
            t2.start();
        }
    }
    

    获取线程名称和设置名字

    获取名字:通过getName()方法获取线程的名称。

    设置名字:1、通过构造方法,可以传入String类型的名字;2、通过SetName(String str)方法设置名字。

    注意:每个线程都有一个默认的名字。

    获取当前线程对象

    式例代码如下:

    package demo01;
    
    public class Demo05 {
    
        public static void main(String[] args) {
            
            System.out.println("当前线程的对象"+Thread.currentThread().getClass());
            MyThread1 mt = new MyThread1();
            mt.start();
        }
    }
    
    class MyThread1 extends Thread{
    
        @Override
        public void run() {
            System.out.println("当前线程的对象"+Thread.currentThread().getClass());
        }
        
    }
    

    Thread.sleep(m)的使用

    式例代码如下:

    package demo02;
    
    public class Demo01 {
    
        public static void main(String[] args) {
            
            test1();
            System.out.println("会在test1方法执行完后,执行该行代码");
            test2();
            System.out.println("会在test2方法执行之前执行该代码");
            
        }
        
        static void test1(){
            for(int i=0; i<10; i++) {
                System.out.println("主线程打印:"+i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        
        static void test2() {
            new Thread() {
                @Override
                public void run() {
                    for(int i=0; i<10; i++) {
                        System.out.println("子线程打印:"+i);
                        try {
                            this.sleep(1000);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            }.start();
        }
    }
    

    一个时间倒计时的式例,代码如下:

    package demo02;
    
    public class Demo02 {
        
        public static void main(String[] args) {
            
            //倒计时式例
            new Thread(new Runnable() {
                int m = 60;//注意,该变量不能提到括号外面,因为局部内部类的变量必须要用final修饰,而final修饰的变量只能赋值一次。
                @Override
                public void run() {
                    for(int i=0; i<m; i++) {
                        System.out.println(m-i);
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                    
                }
            }).start();;
            
        }
    }
    

    守护线程

    作用:当非守护线程执行完毕后,守护线程不管是否执行完毕,都会被停止。

    式例代码如下:

    package demo02;
    
    public class Demo03 {
    
        public static void main(String[] args) {
            //守护线程式例
            Thread t1 = new Thread() {
                @Override
                public void run() {
                    for(int i=0; i<5; i++) {
                        System.out.println("线程---女"+i);
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            };
            
            Thread t2 = new Thread() {
                @Override
                public void run() {
                    for(int i=0; i<15; i++) {
                        System.out.println("线程---男"+i);
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            };
            
            t1.start();
            t2.setDaemon(true);
            t2.start();
        }
    }
    

    线程加入

    式例代码如下:

    package demo02;
    
    public class Demo04 {
    
        public static void main(String[] args) {
            
            Thread t1 = new Thread() {
                @Override
                public void run() {
                    for(int i=0; i<100; i++) {
                        System.out.println("线程A----"+i);
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            };
            
            Thread t2 = new Thread() {
                @Override
                public void run() {
                    for(int i=0; i<100; i++) {
                        if(i == 10) {
                            try {
    //                          t1.join(); //将线程A加入进来,且让线程A执行完后,再继续执行线程B
                                t1.join(100); //将线程A加入进来,且让线程A执行100毫秒后,再继续执行线程B
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                        }
                        System.out.println("线程B----"+i);
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            };
            
            t1.start();
            t2.start();
        }
    }
    

    设置线程优先级

    式例代码如下:

    package demo02;
    
    public class Demo05 {
    
        public static void main(String[] args) {
            
            Thread t1 = new Thread() {
                public void run() {
                    for(int i=0; i<100; i++) {
                        System.out.println("线程A----"+i);
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                };
            };
            
            Thread t2 = new Thread() {
                public void run() {
                    for(int i=0; i<100; i++) {
                        System.out.println("线程B----"+i);
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                };
            };
            
            //设置线程优先级,如果不设置,默认5
            t1.setPriority(Thread.MIN_PRIORITY);
            t2.setPriority(Thread.MAX_PRIORITY);
            t1.start();
            t2.start();
        }
    }
    

    同步锁

    火车售票式例代码如下:

    package demo02;
    
    public class Demo06 {
    
        public static void main(String[] args) {
            //使用同步锁实现卖火车票功能
            Ticket ticket = new Ticket();
            
            Thread t1 = new Thread(ticket, "1");
            Thread t2 = new Thread(ticket, "2");
            Thread t3 = new Thread(ticket, "3");
            Thread t4 = new Thread(ticket, "4");
            
            t1.start();
            t2.start();
            t3.start();
            t4.start();
        }
        
    }
    
    class Ticket implements Runnable{
    
        int ticket = 1000;
        
        @Override
        public void run() {
            
            while(true) {
                synchronized (this) {
                    if(ticket >0) {
                        System.out.println("您在窗口:"+Thread.currentThread().getName()+"购票成功,票号为:"+ticket);
                        ticket --;
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }else {
                        System.out.println("您在窗口:"+Thread.currentThread().getName()+"购票失败,票已售完!");
                        break;
                    }
                }
            }
            
        }
        
        
    }
    

    同步方法

    式例代码如下:

    package demo03;
    
    public class Demo01 {
    
        public static void main(String[] args) {
            
            /**
             * 线程锁的注意事项:
             * 1.线程的锁对象必须要为同一个对象,否则会出现线程安全问题。
             * 2.线程的锁对象,可以使用一个字节码对象,如String.class,能保证锁对象为同一个对象,应为在内存中只有一个字节码对象。
             * 3.在使用同步方法时,如果时静态方法,则锁对象为该方法所属类的字节码对象,如果为非静态方法,则锁对象为this。
             */
            
            MyThread myThread = new MyThread();
            
            Thread t1 = new Thread(myThread,"1");
            Thread t2 = new Thread(myThread,"2");
            Thread t3 = new Thread(myThread,"3");
            Thread t4 = new Thread(myThread,"4");
            
            t1.start();
            t2.start();
            t3.start();
            t4.start();
        }
    }
    
    class MyThread implements Runnable{
    
        private int sum = 100;
        
        //线程同步方法的使用
        /**
         * 线程加锁原则:在保证业务正确的情况下,加锁的代码越少越好,有利于提高效率。减少其他线程的等待时间。
         */
        @Override
        public synchronized void run() {
            
            while(true) {
                if(sum ==0) {
                    System.out.println("票已经卖完!");
                    break;
                }else {
                    System.out.println(Thread.currentThread().getName()+"号窗口出售了“"+sum+"”号车票!");
                    sum--;
                }
            }
        }
        
    }
    

    死锁

    死锁的式例代码如下:

    package demo03;
    
    public class Demo02 {
    
        private static String a= "A";
        private static String b= "B";
        
        public static void main(String[] args) {
            
            /*
             * 死锁:多线程的时候,如果同步代码块嵌套,使用相同的锁,就有可能出现死锁。
             * 以下是一个死锁的实例
             */
    
            new Thread() {
                public void run() {
                    while(true) {
                        synchronized (a) {
                            System.out.println("线程A拿到了A等待B");
                            synchronized (b) {
                                System.out.println("线程A拿到了B,A和B在一起了!");
                            }
                        }
                    }
                };
            }.start();
            
            new Thread() {
                public void run() {
                    while(true) {
                        synchronized (b) {
                            System.out.println("线程B拿到了B等待A");
                            synchronized (a) {
                                System.out.println("线程B拿到了A,A和B在一起了!");
                            }
                        }
                    }
                };
            }.start();
        }
    }
    

    相关文章

      网友评论

          本文标题:Java多线程笔记

          本文链接:https://www.haomeiwen.com/subject/sfajrftx.html