zoukankan      html  css  js  c++  java
  • 关于JAVA线程的学习

    关于JAVA线程的学习

    4、多线程

    4.1、什么是进程?什么是线程?
    进程是一个应用程序(1个进程是一个软件)。
    线程是一个进程中的执行场景/执行单元。
    一个进程可以启动多个线程。

    4.2、对于java程序来说,当在DOS命令窗口中输入:
    java HelloWorld 回车之后。
    会先启动JVM,而JVM就是一个进程。
    JVM再启动一个主线程调用main方法。
    同时再启动一个垃圾回收线程负责看护,回收垃圾。
    最起码,现在的java程序中至少有两个线程并发,
    一个是垃圾回收线程,一个是执行main方法的主线程。

    4.3、进程和线程是什么关系?举个例子

    阿里巴巴:进程
    马云:阿里巴巴的一个线程
    童文红:阿里巴巴的一个线程

    京东:进程
    强东:京东的一个线程
    妹妹:京东的一个线程

    进程可以看做是现实生活当中的公司。
    线程可以看做是公司当中的某个员工。

    注意:
    进程A和进程B的内存独立不共享。(阿里巴巴和京东资源不会共享的!)
    魔兽游戏是一个进程
    酷狗音乐是一个进程
    这两个进程是独立的,不共享资源。

    线程A和线程B呢?
    在java语言中:
    线程A和线程B,堆内存和方法区内存共享。
    但是栈内存独立,一个线程一个栈。

    假设启动10个线程,会有10个栈空间,每个栈和每个栈之间,
    互不干扰,各自执行各自的,这就是多线程并发。

    火车站,可以看做是一个进程。
    火车站中的每一个售票窗口可以看做是一个线程。
    我在窗口1购票,你可以在窗口2购票,你不需要等我,我也不需要等你。
    所以多线程并发可以提高效率。

    java中之所以有多线程机制,目的就是为了提高程序的处理效率。

    4.4、思考一个问题:
    使用了多线程机制之后,main方法结束,是不是有可能程序也不会结束。
    main方法结束只是主线程结束了,主栈空了,其它的栈(线程)可能还在
    压栈弹栈。

    4.5、分析一个问题:对于单核的CPU来说,真的可以做到真正的多线程并发吗?

    对于多核的CPU电脑来说,真正的多线程并发是没问题的。
    4核CPU表示同一个时间点上,可以真正的有4个进程并发执行。

    什么是真正的多线程并发?
    t1线程执行t1的。
    t2线程执行t2的。
    t1不会影响t2,t2也不会影响t1。这叫做真正的多线程并发。

    单核的CPU表示只有一个大脑:
    不能够做到真正的多线程并发,但是可以做到给人一种“多线程并发”的感觉。
    对于单核的CPU来说,在某一个时间点上实际上只能处理一件事情,但是由于
    CPU的处理速度极快,多个线程之间频繁切换执行,跟人来的感觉是:多个事情
    同时在做!!!!!
    线程A:播放音乐
    线程B:运行魔兽游戏
    线程A和线程B频繁切换执行,人类会感觉音乐一直在播放,游戏一直在运行,
    给我们的感觉是同时并发的。

    电影院采用胶卷播放电影,一个胶卷一个胶卷播放速度达到一定程度之后,
    人类的眼睛产生了错觉,感觉是动画的。这说明人类的反应速度很慢,就像
    一根钢针扎到手上,到最终感觉到疼,这个过程是需要“很长的”时间的,在
    这个期间计算机可以进行亿万次的循环。所以计算机的执行速度很快。

    5、java语言中,实现线程有两种方式,那两种方式呢?

    java支持多线程机制。并且java已经将多线程实现了,我们只需要继承就行了。

    第一种方式:编写一个类,直接继承java.lang.Thread,重写run方法。
       // 定义线程类
         public class MyThread extends Thread{
              public void run(){

            }
       }
       // 创建线程对象
       MyThread t = new MyThread();
       // 启动线程。
       t.start();

     1 public class ThreadTest01 {
     2 
     3     public static void main(String[] args) {
     4         //main方法,这里代码属于主线程
     5         //新建分支线程
     6         MyThread myThread= new MyThread();
     7         
     8         //启动线程 start()启动一个分支线程,在JVM中开辟空间,只是为了开辟新空间,这段代码一瞬间消失
     9         //启动成功就会消失,会直接调用run方法,并且run方法在分支的栈底部(压栈)
    10         //run方法和main方法平级
    11         
    12         myThread.start();
    13         //运行在主支栈
    14         for(int i=0;i<1000;i++){
    15             System.out.println("主线程---->"+i);
    16             }
    17           }
    18        }
    19 class MyThread extends Thread{
    20     @Override
    21     public void run() {
    22       //运行在分支栈
    23         for(int i=0;i<1000;i++){
    24             System.out.println("分支线程---->"+i);
    25         }
    26     }
    27 }


    第二种方式:编写一个类,实现java.lang.Runnable接口,实现run方法。
       // 定义一个可运行的类
            public class MyRunnable implements Runnable {
                 public void run(){

              }
            }
        // 创建线程对象
             Thread t = new Thread(new MyRunnable());
             // 启动线程
               t.start();

     1 public class ThreadTest02 {
     2 
     3     public static void main(String[] args) {
     4         //创建一个可运行对象
     5         //MyRunnable r=new MyRunnable();
     6         //将可运行的对象封装成一个线程对象
     7         //Thread t=new Thread(r);
     8         Thread t=new Thread(new MyRunnable());//合并代码
     9         //启动线程
    10         t.start();
    11         //运行在主支栈
    12             for(int i=0;i<100;i++){
    13             System.out.println("主线程---->"+i);
    14         }
    15                   
    16         
    17     }
    18 
    19     
    20 }
    21 //这并不是一个线程,是一个可运行的类.他还不是一个线程
    22 class MyRunnable implements Runnable{
    23 
    24     @Override
    25     public void run() {
    26         //运行在分支支栈
    27             for(int i=0;i<100;i++){
    28         System.out.println("分支线程---->"+i);
    29     }
    30                   
    31     }
    32     
    33 }

            通过匿名内部类:

     1 /*
     2  * 采用匿名内部类
     3  */
     4 public class ThreadTest03 {
     5 
     6     public static void main(String[] args) {
     7         //创建线程对象,采用匿名内部类方式
     8         //这是通过一个没有名字的类new出来的对象
     9     Thread t=new Thread(new Runnable() {
    10         
    11         @Override
    12         public void run() {
    13             
    14             //运行在支栈
    15                 for(int i=0;i<100;i++){
    16                 System.out.println("分支线程---->"+i);
    17             }
    18         }
    19     });
    20     //启动线程
    21     t.start();
    22     
    23     //运行在主栈
    24     for(int i=0;i<100;i++){
    25     System.out.println("主线程---->"+i);
    26 }
    27     
    28     }
    29 
    30 }   

       注意:第二种方式实现接口比较常用,因为一个类实现了接口,它还可以去继承
                    其它的类,更灵活。

    6、关于线程对象的生命周期?
         新建状态
         就绪状态
         运行状态
         阻塞状态
         死亡状态

    线程第二天学习

    1、(这部分内容属于了解)关于线程的调度

    1.1、常见的线程调度模型有哪些?

       抢占式调度模型:
      那个线程的优先级比较高,抢到的CPU时间片的概率就高一些/多一些。
      java采用的就是抢占式调度模型。

       均分式调度模型:
      平均分配CPU时间片。每个线程占有的CPU时间片时间长度一样。
      平均分配,一切平等。
      有一些编程语言,线程调度模型采用的是这种方式。

       怎么获取线程:

    public static void main(String[] args) {
            //获取当前线程
            Thread tt=Thread.currentThread();
            System.out.println(tt.getName());
            //创建线程对象
            MyThread2 t=new MyThread2();
            t.setName("t1");
            String name = t.getName();
            System.out.println(name);
            t.start();
            MyThread2 t2=new MyThread2();
            System.out.println(t2.getName());
            t2.start();
        }
    
    }
    class MyThread2 extends Thread{
            public void  run(){
                Thread ttt=Thread.currentThread();
                System.out.println(ttt.getName());
                //运行在支栈
                for(int i=0;i<100;i++){
                    System.out.println(ttt.getName()+"分支线程---->"+i);
                
                }
                  }
            }
    View Code

    1.2、java中提供了哪些方法是和线程调度有关系的呢?

      实例方法:
         void setPriority(int newPriority) 设置线程的优先级
         int getPriority() 获取线程优先级
             最低优先级1
             默认优先级是5
             最高优先级10

     1 /**
     2  * 关于线程优先级
     3  * @author yumu
     4  *
     5  */
     6 public class ThreadTest09 {
     7 
     8     public static void main(String[] args) {
     9     //设置main支线为1
    10     Thread.currentThread().setPriority(1);
    11     //获取当前优先级
    12     Thread cru=Thread.currentThread();    
    13     System.out.println(cru.getName());
    14     Thread t=new Thread(new MyRunnable5());
    15     //设置分线优先级为10
    16     t.setPriority(10);
    17     t.setName("t");
    18     t.start();
    19     //优先级高,抢到CPU时间相对多一些,运行时间多一些
    20     for(int i=0;i<1000;i++){
    21         System.out.println(Thread.currentThread().getName()+i);
    22         
    23     }
    24     }
    25 
    26 }
    27 class MyRunnable5 implements Runnable{
    28 
    29     @Override
    30     public void run() {
    31         //获取线程优先级
    32         //System.out.println(Thread.currentThread().getName()+"线程优先级是"+Thread.currentThread());
    33         for(int i=0;i<1000;i++){        
    34             System.out.println(Thread.currentThread().getName()+i);
    35         }
    36     }
    37     
    38 }


         优先级比较高的获取CPU时间片可能会多一些。(但也不完全是,大概率是多的。)

       静态方法:

       static void sleep(long millis): 

                   使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。 
     1 /**
     2  * 关于线程sleep方法
     3  *  static void sleep(long millis)
     4  *    1.静态方法:
     5  *    2.参数毫秒
     6  *    3.让当前线程进入休眠,进入阻塞状态
     7  *    4.Thread.sleep(),固定时间调用某方法
     8  *    
     9  * @author yumu
    10  *
    11  */
    12 public class ThreadTest05 {
    13 
    14     public static void main(String[] args) throws InterruptedException {
    15         Thread.sleep(1000*5);
    16     System.out.println("5秒后执行");
    17     
    18     for(int i=0;i<10;i++){
    19         System.out.println(Thread.currentThread().getName()+"----->"+i);
    20         //睡眠一秒
    21         Thread.sleep(1000);
    22         
    23     }
    24     }
    25 }

    如何中断sleep();

     1 /**
     2  * 中断sleep的睡眠
     3  * @author yumu
     4  * 在java中怎么强制终止线程的执行
     5  * 
     6  * t.stop()  //已过时,不建议使用
     7  *   这种方式容易丢失数据,不建议使用,因为直接杀死线程
     8  * t.interrupt();  //干扰睡眠  
     9  *
    10  */
    11 public class ThreadTest07 {
    12 
    13     public static void main(String[] args) {
    14         Thread t=new Thread(new MyRunnable2());
    15         t.setName("t");
    16         t.start();
    17         
    18         try {
    19             Thread.sleep(1000*5);
    20         } catch (InterruptedException e) {
    21             
    22             e.printStackTrace();
    23         }
    24         //中断线程的睡眠  (靠的是异常处理机制)
    25         t.interrupt();  //干扰睡眠  
    26     }
    27 
    28 }
    29 class MyRunnable2 implements Runnable{
    30 
    31     @Override
    32     public void run() {
    33         System.out.println(Thread.currentThread().getName()+"----->begin");
    34         try {
    35             Thread.sleep(1000*60*60*24);
    36         } catch (InterruptedException e) {
    37             
    38             e.printStackTrace();
    39         }
    40         System.out.println(Thread.currentThread().getName()+"----->end");
    41     }
    42     
    43 }

     如何合理的终止线程:  打一个布尔标记

     1 /**
     2  * 如何合理的终止线程
     3  * @author yumu
     4  *
     5  */
     6 public class ThreadTest08 {
     7 
     8     public static void main(String[] args) {
     9         MyRunnable3 r=new MyRunnable3();
    10         Thread t=new Thread(r);
    11         t.start();
    12         
    13         try {
    14             Thread.sleep(1000*5);
    15         } catch (InterruptedException e) {
    16             
    17             e.printStackTrace();
    18         }
    19         //终止线程.改标记
    20         r.run=false;
    21         
    22     }
    23 
    24 }
    25 class MyRunnable3 implements Runnable{
    26     //打一个布尔标记
    27     boolean run=true;
    28     @Override
    29     public void run() {
    30         
    31         for(int i=0;i<10;i++){
    32             if(run){
    33             System.out.println(Thread.currentThread().getName()+"--->"+i);
    34             try {
    35                 Thread.sleep(1000);
    36         } catch (InterruptedException e) {
    37                 
    38                 e.printStackTrace();
    39             }
    40         
    41         }else{
    42             
    43             
    44             return;
    45         }
    46     }
    47     }
    48 }

            static void yield() 让位方法
          暂停当前正在执行的线程对象,并执行其他线程
             yield()方法不是阻塞方法。让当前线程让位,让给其它线程使用。
             yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”。

    1 for(int i=0;i<1000;i++){
    2             //每100让位一次
    3             if(i%100==0){
    4                 Thread.yield();//当前线程暂停一下,给主线程让一下
    5             }
    6             System.out.println(Thread.currentThread().getName()+i);
    7         }


    注意:在回到就绪之后,有可能还会再次抢到。

       实例方法:
           void join()
          合并线程
               class MyThread1 extends Thread {
               public void doSome(){
                      MyThread2 t = new MyThread2();
                          t.join(); // 当前线程进入阻塞,t线程执行,直到t线程结束。当前线程才可以继续。
                   }
              }

               class MyThread2 extends Thread{

         }

     1 /**
     2  * 合并线程  join()
     3  * @author yumu
     4  *
     5  */
     6 public class ThreadTest10 {
     7 
     8     public static void main(String[] args) {
     9         System.out.println("main begin");
    10         Thread t=new Thread(new MyRunnable6());
    11         t.setName("t");
    12         t.start();
    13         try {
    14             t.join();  //t线程合并到当前线程
    15         } catch (InterruptedException e) {
    16             
    17             e.printStackTrace();
    18         }
    19         System.out.println("main over");
    20     }
    21 
    22 }
    23 class MyRunnable6 implements Runnable{
    24 
    25     @Override
    26     public void run() {
    27         for(int i=1;i<5;i++){
    28             System.out.println(Thread.currentThread().getName());
    29         }
    30     }
    31     
    32 }

    2、关于多线程并发环境下,数据的安全问题。(重点)

    2.1、为什么这个是重点?
     以后在开发中,我们的项目都是运行在服务器当中,
     而服务器已经将线程的定义,线程对象的创建,线程
     的启动等,都已经实现完了。这些代码我们都不需要
     编写。

    最重要的是:你要知道,你编写的程序需要放到一个
    多线程的环境下运行,你更需要关注的是这些数据
    在多线程并发的环境下是否是安全的。(重点:*****)

    2.2、什么时候数据在多线程并发的环境下会存在安全问题呢?
    三个条件:
    条件1:多线程并发。
    条件2:有共享数据。
    条件3:共享数据有修改的行为。

    满足以上3个条件之后,就会存在线程安全问题。

    2.3、怎么解决线程安全问题呢?
    当多线程并发的环境下,有共享数据,并且这个数据还会被修改,此时就存在
    线程安全问题,怎么解决这个问题?
    线程排队执行。(不能并发)。
    用排队执行解决线程安全问题。
    这种机制被称为:线程同步机制。

    专业术语叫做:线程同步,实际上就是线程不能并发了,线程必须排队执行。

    怎么解决线程安全问题呀?
    使用“线程同步机制”。

    线程同步就是线程排队了,线程排队了就会牺牲一部分效率,没办法,数据安全
    第一位,只有数据安全了,我们才可以谈效率。数据不安全,没有效率的事儿。

    案例:模拟银行取款:

     1 package javase.threadsafe;
     2 /*
     3  * 银行账户
     4  */
     5 public class Account {
     6     //账号
     7     private String actno;
     8     //余额
     9     private double balance;
    10     public Account(String actno, double balance) {
    11         super();
    12         this.actno = actno;
    13         this.balance = balance;
    14     }
    15     public Account() {
    16         super();
    17         
    18     }
    19     public String getActno() {
    20         return actno;
    21     }
    22     public void setActno(String actno) {
    23         this.actno = actno;
    24     }
    25     public double getBalance() {
    26         return balance;
    27     }
    28     public void setBalance(double balance) {
    29         this.balance = balance;
    30     }
    31     @Override
    32     public String toString() {
    33         return "Account [actno=" + actno + ", balance=" + balance + "]";
    34     }
    35     //取款方法
    36     public void withdraw(double money){
    37         //取款之前的余额
    38         double before=this.getBalance();
    39         //取款之后的余额
    40         double after =before-money;
    41         //模拟网络延迟
    42         try {
    43             Thread.sleep(1000);
    44         } catch (InterruptedException e) {
    45             
    46             e.printStackTrace();
    47         }
    48         
    49         //更新余额
    50         this.setBalance(after);
    51     }
    52 }

    取款:

     1 package javase.threadsafe;
     2 
     3 public class AccountThread extends Thread{
     4     //两个对象必须共享一个账户对象
     5     private Account act;
     6     
     7     //通过构造方法传递过来账户信息
     8     public AccountThread(Account act){
     9         this.act=act;
    10     }
    11     public void run(){
    12         //表示取款的操作
    13         //假设取款2000
    14         double money=5000;
    15         //取款,多线程并发
    16         act.withdraw(money);
    17         System.out.println(Thread.currentThread().getName()+"对"+act.getActno()+"取款"+money+"成功.余额"+act.getBalance());
    18     }
    19 }
    package javase.threadsafe;
    
    public class Test {
    
        public static void main(String[] args) {
            //创建账户对象
            Account act=new Account("act-001",10000);
            //创建两个线程
            Thread t1=new AccountThread(act);
            Thread t2=new AccountThread(act);
            //设置name
            t1.setName("t1");
            t2.setName("t2");
            //启动线程
            t1.start();
            t2.start();
        }
    
    }

     出现异常,共10000元,取款成功两次,余额5000,

    未使用线程同步机制:

     1 package javase.threadsafe2;
     2 /*
     3  * 银行账户
     4  */
     5 public class Account {
     6     //账号
     7     private String actno;
     8     //余额
     9     //对象
    10     Object obj=new Object();
    11     
    12     private double balance;
    13     public Account(String actno, double balance) {
    14         super();
    15         this.actno = actno;
    16         this.balance = balance;
    17     }
    18     public Account() {
    19         super();
    20         
    21     }
    22     public String getActno() {
    23         return actno;
    24     }
    25     public void setActno(String actno) {
    26         this.actno = actno;
    27     }
    28     public double getBalance() {
    29         return balance;
    30     }
    31     public void setBalance(double balance) {
    32         this.balance = balance;
    33     }
    34     @Override
    35     public String toString() {
    36         return "Account [actno=" + actno + ", balance=" + balance + "]";
    37     }
    38     //取款方法
    39     public void withdraw(double money){
    40         //以下代码必须是线程排队的,不能并发
    41         /*线程同步机制语法:
    42          * synchronized(){
    43          *    同步代码块;
    44          *   }
    45          * synchronized后面小括号里面传的数据很重要
    46          *   这个数据必须是多线程共享的数据,才能到达多线程排队
    47          *   小括号写什么?
    48          *      多个线程必须为多个对象共享对象,不需要共享的不需要写
    49          *  java语言中每个对象都有"一把锁",其实这把锁就是标记
    50          *    1个对象1把锁  
    51          *  以下代码执行原理:
    52          *     1.
    53          *    
    54          */
    55         //这里共享对象是账户对象  this obj actno 
    56         //public synchronized void withdraw(double money){ 
    57         //synchronized出现在实例方法 这是锁一定是你this 
    58         //表示整个方法都要同步,可能会无故扩大同步范围,影响效率
    59         //好处L代码少,简洁了
    60         synchronized (actno) {
    61             //取款之前的余额
    62             double before=this.getBalance();
    63             //取款之后的余额
    64             double after =before-money;
    65             //模拟网络延迟
    66             try {
    67                 Thread.sleep(1000);
    68             } catch (InterruptedException e) {
    69                 
    70                 e.printStackTrace();
    71             }
    72             
    73             //更新余额
    74             this.setBalance(after);
    75         }
    76         
    77     }
    78 }

    使用线程同步机制:
    2.4、说到线程同步这块,涉及到这两个专业术语:

    异步编程模型:
    线程t1和线程t2,各自执行各自的,t1不管t2,t2不管t1,
    谁也不需要等谁,这种编程模型叫做:异步编程模型。
    其实就是:多线程并发(效率较高。)

    异步就是并发。

    同步编程模型:
    线程t1和线程t2,在线程t1执行的时候,必须等待t2线程执行
    结束,或者说在t2线程执行的时候,必须等待t1线程执行结束,
    两个线程之间发生了等待关系,这就是同步编程模型。
    效率较低。线程排队执行。

    同步就是排队。

    3、Java中有三大变量?【重要的内容。】

    实例变量:在堆中。

    静态变量:在方法区。

    局部变量:在栈中。

    以上三大变量中:
    局部变量永远都不会存在线程安全问题。
    因为局部变量不共享。(一个线程一个栈。)
    局部变量在栈中。所以局部变量永远都不会共享。

    实例变量在堆中,堆只有1个。
    静态变量在方法区中,方法区只有1个。
    堆和方法区都是多线程共享的,所以可能存在线程安全问题。

    局部变量+常量:不会有线程安全问题。
    成员变量:可能会有线程安全问题。

    4、如果使用局部变量的话:
    建议使用:StringBuilder。
    因为局部变量不存在线程安全问题。选择StringBuilder。
    StringBuffer效率比较低。

    ArrayList是非线程安全的。
    Vector是线程安全的。
    HashMap HashSet是非线程安全的。
    Hashtable是线程安全的。

    5、总结:

     synchronized有三种写法:

    第一种:同步代码块
    灵活
    synchronized(线程共享对象){
             同步代码块;
    }

    第二种:在实例方法上使用synchronized
    表示共享对象一定是this
    并且同步代码块是整个方法体。

    第三种:在静态方法上使用synchronized
    表示找类锁。
    类锁永远只有1把。
    就算创建了100个对象,那类锁也只有一把。

     

    6、聊一聊,我们以后开发中应该怎么解决线程安全问题?

    是一上来就选择线程同步吗?synchronized
    不是,synchronized会让程序的执行效率降低,用户体验不好。
    系统的用户吞吐量降低。用户体验差。在不得已的情况下再选择
    线程同步机制。

    第一种方案:尽量使用局部变量代替“实例变量和静态变量”。

    第二种方案:如果必须是实例变量,那么可以考虑创建多个对象,这样
    实例变量的内存就不共享了。(一个线程对应1个对象,100个线程对应100个对象,
    对象不共享,就没有数据安全问题了。)

    第三种方案:如果不能使用局部变量,对象也不能创建多个,这个时候
    就只能选择synchronized了。线程同步机制。

    7、线程这块还有那些内容呢?列举一下
    7.1、守护线程

    java语言中线程分为两大类:
    一类是:用户线程
    一类是:守护线程(后台线程)
    其中具有代表性的就是:垃圾回收线程(守护线程)。

     

    守护线程的特点:
    一般守护线程是一个死循环,所有的用户线程只要结束,
    守护线程自动结束。

     1 /**
     2  * 守护线程
     3  * @author yumu
     4  *
     5  */
     6 public class ThreadTest011 {
     7 
     8     public static void main(String[] args) {
     9         Thread t =new  BackDataThread();
    10         t.setName("守护线程");  //主线程结束,t(守护线程)也结束
    11         t.setDaemon(true);
    12         t.start();
    13         //主线程
    14         for(int i=0;i<10;i++){
    15             System.out.println(Thread.currentThread().getName());
    16             try {
    17                 Thread.sleep(1000);
    18             } catch (InterruptedException e) {
    19                 
    20                 e.printStackTrace();
    21             }
    22             
    23             
    24         }
    25         
    26         
    27     }
    28 
    29 }
    30 class BackDataThread extends Thread{
    31     public  void  run(){
    32         int i=0;
    33         while(true){
    34             System.out.println(Thread.currentThread().getName()+"--->"+(++i));
    35             
    36             try {
    37                 Thread.sleep(1000);
    38             } catch (InterruptedException e) {
    39                 
    40                 e.printStackTrace();
    41             }
    42         }
    43     }
    44 }


    注意:主线程main方法是一个用户线程。

     

    守护线程用在什么地方呢?
    每天00:00的时候系统数据自动备份。
    这个需要使用到定时器,并且我们可以将定时器设置为守护线程。
    一直在那里看着,没到00:00的时候就备份一次。所有的用户线程
    如果结束了,守护线程自动退出,没有必要进行数据备份了。

     

    7.2、定时器

    定时器的作用:
    间隔特定的时间,执行特定的程序。

     

    每周要进行银行账户的总账操作。
    每天要进行数据的备份操作。

     

    在实际的开发中,每隔多久执行一段特定的程序,这种需求是很常见的,
    那么在java中其实可以采用多种方式实现:

    可以使用sleep方法,睡眠,设置睡眠时间,没到这个时间点醒来,执行
    任务。这种方式是最原始的定时器。(比较low)

     

    在java的类库中已经写好了一个定时器:java.util.Timer,可以直接拿来用。
    不过,这种方式在目前的开发中也很少用,因为现在有很多高级框架都是支持
    定时任务的。

     

    在实际的开发中,目前使用较多的是Spring框架中提供的SpringTask框架,
    这个框架只要进行简单的配置,就可以完成定时器的任务。

     1 /**
     2  * 定时器
     3  * @author yumu
     4  *
     5  */
     6 public class TimerTest {
     7 
     8     public static void main(String[] args) throws ParseException {
     9         //创建定时器对象
    10         Timer timer=new Timer();
    11         //Timer timer=new Timer(true);  //守护线程方式
    12         //指定定时任务
    13         //timer.schedule(定时任务, firstTime, 间隔时间);
    14         SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    15         Date firstTime = sdf.parse("2020-10-20 19:57:23");
    16         timer.schedule(new logTimerTask(), firstTime, 1000*10);
    17     
    18     }
    19 
    20 }
    21 //编写一个定时任务类
    22 //假设这是一个记录日志的定时任务
    23 class logTimerTask extends TimerTask{
    24 
    25     @Override
    26     public void run() {
    27         //编写需要执行的任务就行
    28         SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    29         String strTime = sdf.format(new Date());
    30         System.out.println(strTime+",完成了一次数据备份!");
    31     }
    32     
    33 }

    每隔十秒记录日志:


    7.3、实现线程的第三种方式:FutureTask方式,实现Callable接口。(JDK8新特性。)

    1.3、实现线程的第三种方式:实现Callable接口。(JDK8新特性。)


    这种方式实现的线程可以获取线程的返回值。
    之前讲解的那两种方式是无法获取线程返回值的,因为run方法返回void。

    思考:
    系统委派一个线程去执行一个任务,该线程执行完任务之后,可能
    会有一个执行结果,我们怎么能拿到这个执行结果呢?
    使用第三种方式:实现Callable接口方式。

     1 /**
     2  * 实现线程的第三种方式
     3  *   实现:callable接口
     4  *   优点:可以回去线程的执行结果
     5  *   缺点:效率低
     6  * @author yumu
     7  *
     8  */
     9 public class ThreadTest11 {
    10 
    11     @SuppressWarnings({ "unchecked", "rawtypes" })
    12     public static void main(String[] args) throws InterruptedException, ExecutionException {
    13         //创建一个"未来任务对象"
    14         FutureTask task=new FutureTask(new Callable() {
    15 
    16             @Override
    17             public Object call() throws Exception { //calll()相当于run()方法
    18                 //线程执行一个任务,结束后可能会有一个执行结果
    19                 //模拟执行
    20                 System.out.println("call method begin");
    21                 Thread.sleep(1000*10);
    22                 System.out.println("call method end");
    23                 int a=100;
    24                 int b=200;
    25                 return a+b;
    26             }
    27         });
    28         //创建线程对象
    29         Thread t=new Thread(task);
    30         t.start();
    31         //这里是main方法,这是在主线程中
    32         Object obj=task.get();  //让当前线程阻塞,main
    33         System.out.println(obj);
    34         System.out.println("终于结束了");
    35     }
    36 
    37 }


    7.4、关于Object类中的wait和notify方法。(生产者和消费者模式!)

    第一:wait和notify方法不是线程对象的方法,是java中任何一个java对象
    都有的方法,因为这两个方式是Object类中自带的。
    wait方法和notify方法不是通过线程对象调用,
    不是这样的:t.wait(),也不是这样的:t.notify()..不对。

    第二:wait()方法作用?
    Object o = new Object();
    o.wait();

    表示:
    让正在o对象上活动的线程进入等待状态,无期限等待,
    直到被唤醒为止。
    o.wait();方法的调用,会让“当前线程(正在o对象上
    活动的线程)”进入等待状态。

    第三:notify()方法作用?
    Object o = new Object();
    o.notify();

    表示:
    唤醒正在o对象上等待的线程。

    还有一个notifyAll()方法:
    这个方法是唤醒o对象上处于等待的所有线程。

     1 /**
     2  * 1.使用wait()方法和notify()方式实现生产者和消费者模式
     3  * 2."生产者消费者模式,一种特殊的业务需求"
     4  * 3.wait()和notify() 不是线程对象的方法,是普通JAVA对象都有的方法
     5  * 4.建立在线程同步基础上.要操作一个仓库,有线程安全问题
     6  * 5.wait()作用:o.wait()让o对象上的线程t进入等待状态,并且释放t之前占有的o对象的锁
     7  * 6.notify()作用:让o对象等待的线程唤醒,只是通知,不会释放o对象之前占有的对象的锁
     8  * 
     9  * 7.模拟这样一个需求:
    10  *    产库:list集合
    11  *    list集合假设只能存一个元素
    12  *    1个元素就表示仓库满了
    13  *     如果list元素个数为0表示仓库空了
    14  *     保证list集合永远存一个元素
    15  * @author yumu
    16  *
    17  */
    18 public class ThreadTest1 {
    19 @SuppressWarnings("rawtypes")
    20 public static void main(String[] args) {
    21     List list=new ArrayList();
    22     Thread t1=new  Thread(new Producer(list));
    23     Thread t2=new  Thread(new Consumer(list));
    24     t1.setName("生产者线程");
    25     t2.setName("消费者线程");
    26     t1.start();
    27     t2.start();
    28     
    29 }
    30 }
    31 //生产线程
    32 class Producer implements Runnable{
    33     private List list;
    34     
    35 
    36     public Producer(List list) {
    37         super();
    38         this.list = list;
    39     }
    40 
    41 
    42     @Override
    43     public void run() {
    44         synchronized (list) {
    45             while(true){        
    46             if(list.size()>0){
    47                 //说明集合有元素,进入等待
    48                 try {
    49                     list.wait();
    50                 } catch (InterruptedException e) {
    51                     
    52                     e.printStackTrace();
    53                 }
    54             }
    55             Object obj=new Object();
    56             list.add(obj);
    57             System.out.println(Thread.currentThread().getName()+"---------->"+obj);
    58             list.notifyAll();
    59         }
    60     }
    61     }
    62 }
    63 //消费线程
    64 class Consumer implements Runnable{
    65 private List list;
    66     
    67 
    68     public Consumer(List list) {
    69         super();
    70         this.list = list;
    71     }
    72     @Override
    73     public void run() {
    74         while(true){
    75         synchronized (list) {
    76             if(list.size()==0){
    77                 //说明集合没有元素,进入等待
    78                 try {
    79                     list.wait();
    80                 } catch (InterruptedException e) {
    81                     
    82                     e.printStackTrace();
    83                 }
    84             }
    85             //进行消费
    86             Object obj = list.remove(0);
    87             System.out.println(Thread.currentThread().getName()+"---------->"+obj);
    88             list.notifyAll();
    89         }
    90     }    
    91 }
    92 }

     关于线程的其他内容:

    面试题:

    练习题:

  • 相关阅读:
    VC常用代码
    richedit
    vc++ 2005 发布程序
    管道应用之捕获控制台程序信息
    黑客基础知识编程(转)
    Get All IE Info from win32 api
    vc 界面编程常用方法(http://blog.emuch.net/244485/spacelistblogitemtypeid2708.html)
    使用ADO调用存储过程
    在C#中如何实现文件夹的复制(转)
    C语言开发病毒程序(转)
  • 原文地址:https://www.cnblogs.com/yumu77/p/13868156.html
Copyright © 2011-2022 走看看