码迷,mamicode.com
首页 > 编程语言 > 详细

关于JAVA线程的学习

时间:2020-11-01 21:15:06      阅读:22      评论:0      收藏:0      [点我收藏+]

标签:语法   tar   ***   内部类   讲解   优先级   新建   auth   共享资源   

关于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语言中线程分为两大类:
一类是:用户线程
一类是:守护线程(后台线程)
其中具有代表性的就是:垃圾回收线程(守护线程)。

 

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

注意:主线程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 }

技术图片

 

 关于线程的其他内容:

面试题:

练习题:

关于JAVA线程的学习

标签:语法   tar   ***   内部类   讲解   优先级   新建   auth   共享资源   

原文地址:https://www.cnblogs.com/yumu77/p/13868156.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!