博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java中的多线程,线程池
阅读量:7012 次
发布时间:2019-06-28

本文共 13270 字,大约阅读时间需要 44 分钟。

 

 

Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。Java可以用三种方式来创建线程,如下所示:

1)继承Thread类创建线程

2)实现Runnable接口创建线程

3)使用Callable和Future创建线程

 

一.概念及特点

    1.概念

          进程:计算机运行的应用程序。(多进程作用:提高CPU的使用率,不提高速度);

          线程;一个进程中的执行场景,一个进程可以包多个线程。(多线程作用:提高应用程序的使用率,不提高速度)。

    2.内存特点

          进程和进程之间的内存是独立的;

          线程和线程共享“堆内存的方法区内存”,栈内存是独立的,一个线程一个栈。

    3.java程序的运行原理

         Java命令启动Java虚拟机,启动JVM,等同于启动了一个应用程序,、表示启动了一个进程,该进程会自动启动一个“主线程”,然后主线程去调用某个类的main方法,所 以main方法运行在主线程中。

二.线程的创建和启动

    1.实现多线程的第一种方式: 

         第一步: 继承Java.lang.Thread

         第二步;重写run方法

例:

1 class  Threadtest01 2 { 3     public static void main(String[] args)   //main方法在主线程 4     { 5         //1.创建线程 6         Thread t = new p(); 7         //2.启动线程 8         t.start();      /*启动t线程,执行后,t线程瞬间结束,JVM再分配一个新的栈给t线程 9                           run方法不需要手动调用,系统线程启动之后自动调用run方法。*/10     11        for(int i=0;i<100;i++)   //在主线程中运行12         {13            System.out.println("main....."+i);14         }15     }16 }17 /*18    在多线程中,main方法结束后只是主线程踪没有方法栈帧了,但19    其他线程中或者其他栈中还有栈帧,main方法结束,程序可能还在运行20 */21 22 //3.定义一个线程23 class p extends Thread24 {25     //重写run方法26     public void run ()27     {28          for(int i=0;i<100;i++)29         {30            System.out.println("run...."+i);31         }32     }33 }34 /*35 运行部分结果如下:36 main.....037 main.....138 main.....239 run....040 main.....341 run....142 main.....443 run....244 main.....545 run....346 main.....647 run....448 main.....749 run....550 main.....851 run....652 */
View Code

上述代码的图解如下:

     2.实现多线程的第二种方式:

           第一步:写一个实现类java,lang.Runnable;接口

          第二步:实现run方法

     例:

1 package test; 2  3 class Main  4 { 5     public static void main(String[] args)  6     { 7         //创建线程 8         Thread t = new Thread(new p()); 9         //启动线程10         t.start();11         for(int i = 0;i<100;i++)12               System.out.println("mian..."+i);13         14     }15 }16 //优先,一个类实现接口之外保留了类的继承17 class p implements Runnable18 {19     public void run()20     {21         for(int i = 0;i<100;i++)22           System.out.println("run..."+i);23     }24 }25 /*26  * 部分运行结果:27  * run...828 mian...1029 run...930 mian...1131 run...1032 mian...1233 run...1134 mian...1335 run...1236  */
View Code

 .线程的生命周期

     线程生命周期状态图:

                

 四.线程的调度与控制

     1.线程优先级     

          java虚拟机主要负责则线程的调度,获取CPU的使用权,目前有两种调度模型:分时调度模型和抢占调度模型;java使用抢占调度模型。

             分时调度模型:所有线程轮流使用CPU的使用权,平均分配给每个线程占用CPU的时间片。

 

             抢占调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么随机选择一个,优先极高的线程获得的CPU时间片相对多些。

      例:

1 /* 2    线程优先级高的获取的时间片相对高 3 */ 4 class  Threadtest04 5 { 6     public static void main(String[] args)  7     { 8         //线程优先级 9         System.out.println(Thread.MAX_PRIORITY); //10 优先级最高10         System.out.println(Thread.MIN_PRIORITY); //1  优先级最低11         System.out.println(Thread.NORM_PRIORITY); //5  默认优先级12         Thread t1 = new P();13         t1.setName("t1");14 15         Thread t2 = new P();16         t2.setName("t2");17 18         System.out.println(t1.getPriority()); //默认优先级为519         System.out.println(t2.getPriority()); //默认优先级为520         //设置优先级21         t1.setPriority(4);22         t2.setPriority(9);23 24         System.out.println(t1.getPriority()); //默认优先级为525         System.out.println(t2.getPriority()); //默认优先级为526         //启动线程27         t1.start();28         t2.start();29     }30 }31 32 class P extends Thread33 {34     public void run()35     {36         for(int i=0;i<10;i++)37         {38             System.out.println(Thread.currentThread().getName()+"..."+i);39         }40     }41 }42 /*43 运行结果:44 1045 146 547 548 549 450 951 t1...052 t2...053 t1...154 t2...155 t1...256 t2...257 t1...358 t2...359 t1...460 t2...461 t1...562 t2...563 t1...664 t2...665 t1...766 t2...767 t1...868 t2...869 t1...970 t2...971 请按任意键继续. . .72 73 74 */
View Code

   

    2.sleep方法

     sleep和wait的区别:

      (1)所属类不同  

               Thread.sleep() ;   Object.wait()

      (2)对于线程已经占有资源的处理

              sleep在休息的时候,不释放资源 ; wait在等待的时候释放自己占用的资源

例:

1 /* 2   1.thread.sleep(毫秒) 3   2.sleep方法是一个静态方法 4   3.该方法作用:阻塞当前线程,将CPU让给其他线程 5 */ 6 class Threadtest05  7 { 8     public static void main(String[] args) throws InterruptedException 9     {10         Thread t1 = new P();11         t1.setName("t1");12         t1.start();13         Thread.sleep(5000);14         //t1.interrupt();打断线程的睡眠15         for(int i = 0;i<10;i++)16         {17             System.out.println(Thread.currentThread().getName()+".."+i);18             Thread.sleep(500);//阻塞主线程0.5秒19         }20     }21 }22 23 class P extends Thread24 {25     //被重写的方法不能抛出异常,在run方法的声明为置上不能呢使用Throws,26     public void run()27     {28         for(int i = 0;i<10;i++)29         {30             31             try{32                 Thread.sleep(1000);//让当前线程阻塞1s33             }catch(InterruptedException e)34             {35                 e.printStackTrace();36             }37             System.out.println(Thread.currentThread().getName()+".."+i);38         }39     }40 }41 /*42   运行结果:43   main..044 t1..045 main..146 t1..147 main..248 main..349 main..450 t1..251 main..552 t1..353 main..654 main..755 t1..456 main..857 main..958 t1..559 t1..660 t1..761 t1..862 t1..963 请按任意键继续. . .64 65 */
View Code

 

    3.yield方法

          使用yield方法,与sleep方法类似,不能由用户指定暂停多长时间,并且yield方法只能让同优先级的线程由执行机会,让位时间不固定。静态方法。

    4.Join方法

           合并线程

 五.线程的同步(加锁)

    1.基本概念及特点

       (1)概念:

            a.线程同步,是指某一个时刻,只允许一个线程访问共享资源,线程同步实际上是对对象加锁,如果对象中的方法都是同步方法,那么某一时刻只能执 行一个方法,采用线程同步解决以上问题;  为了数据安全,尽管应用程序的使用效率降低,但是为了数据安全,必须加入线程同步机制,线程同步机制使程序等同于单线程。

              b.异步编程模型:多个线程分别执行,各线程互不影响。

              c.同步编程模型:多个线程执行只有一个线程执行结束才能执行另一个线程。(作用:达到数据安全)

      (2)使用线程同步机制的条件:

              a:必须是多线程环境

              b:多线程环境共享一个数据

              c:共享的数据涉及到数据的修改

  2.举例

 例1:(对象锁,方法一,控制精确,常用)

1 /* 2    模拟银行取款系统:对于同一个账号,用两个线程同时对其取款。 3 */ 4 class Threadtest06 5 { 6     public static void main(String[] args)  7     { 8        //创建公共账号 9        Account act = new Account("账号sss",10000);10        //创建两个线程对同一个账户取款11        Thread t1 = new Thread(new P(act));12        Thread t2 = new Thread(new P(act));13        t1.start();14        t2.start();15     }16 }17 //取款线程18 class P implements Runnable19 {20     //账户21     Account act;22     P(Account act)23     {24         this.act = act;25     }26     public void run()27     {28         act.withdraw(2000);29         System.out.println("取款成功,余额为:"+act.getBalance());30     }31 32 }33 //账户34 class Account35 {36     private String action;37     private double balance;38     public Account(){}39     public Account(String action,double balance){40       this.action = action;41       this.balance = balance;42     }43     public void setAction(String action)44     {45         this.action = action;46     }47     48     public void setBalance(double balance)49     {50         this.balance = balance;51     }52     public String getAction(String action)53     {54         return action;55     }56     public double getBalance()57     {58         return balance;59     }60     public void withdraw(double money)61     {62         synchronized(this){  //this表示共享对象63            double after = balance - money;64            try{65            Thread.sleep(1000);66            }catch(Exception e){}67            this.setBalance(after);68         }69     }70 }71 72 /*73 运行结果:74 取款成功,余额为:8000.075 取款成功,余额为:6000.076 请按任意键继续. . .77 78 */
View Code

     上例原理:   t1线程执行到synchronized关键字处,就会去找this对象锁,如果找到this对象锁,就回进入同步语句块中执行,当同步语句块中的代码执行结束后,

                 t1线程归还this对象锁。在t1线程执行同步语句块的过程中,如果t2线程也过来执行此代码,也遇到synchronized关键字,所以也去找this对象锁,但是

                 该对象锁被t1线程持有,只能等待this对象的归还。

 例2;synchronized关键字添加到成员方法上,线程拿到的扔是this的对象锁(对象锁,方法二,执行效率低)

1 /* 2    模拟银行取款系统:对于同一个账号,用两个线程同时对其取款。 3 */ 4 class Threadtest06 5 { 6     public static void main(String[] args)  7     { 8        //创建公共账号 9        Account act = new Account("账号sss",10000);10        //创建两个线程对同一个账户取款11        Thread t1 = new Thread(new P(act));12        Thread t2 = new Thread(new P(act));13        t1.start();14        t2.start();15     }16 }17 //取款线程18 class P implements Runnable19 {20     //账户21     Account act;22     P(Account act)23     {24         this.act = act;25     }26     public void run()27     {28         act.withdraw(2000);29         System.out.println("取款成功,余额为:"+act.getBalance());30     }31 32 }33 //账户34 class Account35 {36     private String action;37     private double balance;38     public Account(){}39     public Account(String action,double balance){40       this.action = action;41       this.balance = balance;42     }43     public void setAction(String action)44     {45         this.action = action;46     }47     48     public void setBalance(double balance)49     {50         this.balance = balance;51     }52     public String getAction(String action)53     {54         return action;55     }56     public double getBalance()57     {58         return balance;59     }60     public  synchronized void withdraw(double money)61     {62         63            double after = balance - money;64            try{65            Thread.sleep(1000);66            }catch(Exception e){}67            this.setBalance(after);68         69     }70 }71 72 /*73 运行结果:74 取款成功,余额为:8000.075 取款成功,余额为:6000.076 请按任意键继续. . .77 78 */
View Code

  (另:StirngBuffer  Vector Hashtable 是线程安全的 )

 例3:(类锁)

1 /* 2    类锁,类只有一个,所以类锁只有一个,与对象无关 3 */ 4 class Threadtest07  5 { 6     public static void main(String[] args) throws Exception 7     { 8         Thread t1 = new Thread(new P()); 9         Thread t2 = new Thread(new P());10         t1.setName("t1");11         t2.setName("t2");12         t1.start();13         Thread.sleep(1000);14         t2.start();15     }16 }17 18 class P implements Runnable19 {20     public void run()21     {22         if("t1".equals(Thread.currentThread().getName()))23          MyClass.m1();24         if("t2".equals(Thread.currentThread().getName()))25          MyClass.m2();26     }27 }28 29 class MyClass30 {31     //将synchronized添加到静态方法上,线程执行到此方法会找到类锁32     public synchronized static void m1()33     {34         try{35             Thread.sleep(10000);36         }catch(Exception e){}37         System.out.println("m1");38     }39     /*  不会等m1结束,该方法没有被synchronized修饰40     public static void m2()41     {42      System.out.println("m2");43     }44     */45     //m2方法等m1结束之后才能执行,线程执行到该代码需要类锁,二类锁只有一个46     public synchronized static void m2()47     {48      System.out.println("m2");49     }50 }
View Code

六.死锁

   例:  

1 /* 2    死锁 3 */ 4 class DeadLook 5 { 6     public static void main(String[] args)  7     { 8        Object o1 = new Object();     9        Object o2 = new Object();10        11        Thread t1 = new Thread(new T1(o1,o2));12        Thread t2 = new Thread(new T2(o1,o2));13        t1.start();14        t2.start();15     }16 }17 18 class T1 implements Runnable19 {20     Object o1;21     Object o2;22     T1(Object o1,Object o2)23     {24         this.o1 = o1;25         this.o2 = o2;26     }27     public void run()28     {29         synchronized(o1)30         {31             try{Thread.sleep(1000);}catch(Exception e){}32             synchronized(o2)33             {34                 System.out.println("t1");35             }36         }37     }38 }39 class T2 implements Runnable40 {41     Object o1;42     Object o2;43     T2(Object o1,Object o2)44     {45         this.o1 = o1;46         this.o2 = o2;47     }48     public void run()49     {50         synchronized(o2)51         {52             try{Thread.sleep(1000);}catch(Exception e){}53             synchronized(o1)54             {55                 System.out.println("t2");56             }57         }58     }59 }
View Code

七.守护线程

         从线程的分类上可以分为:用户线程和守护线程,所有的用户线程结束生命周期,只有一个用户线程存在,那么守护线程就不会结束,Java中的垃圾回收器就

    是一个守护线程,只有应用程序中所有的线程结束,它才会结束。 

    例:

1 /* 2    守护线程: 3    所有的用户线程结束,守护线程才会结束,守护线程是一个无限循环执行的。 4 */ 5 class  Threadtest08 6 { 7     public static void main(String[] args) throws Exception   8     { 9         Thread t1 = new P();10         t1.setName("t1");11         t1.setDaemon(true);//将用户线程修改为守护线程12         t1.start();13         //主线程14         for(int i = 0;i < 10; i++)15         {16            System.out.println(Thread.currentThread().getName()+".."+i);17            Thread.sleep(1000);18         }19     }20 }21 22 class P extends Thread23 {24    public void run()25     {26        int i = 0;27        while(true)28         {29            i++;30            System.out.println(Thread.currentThread().getName()+".."+i);31            try{Thread.sleep(1000);}catch(Exception e){}32         }33     }34 }35 /*36 运行结果:37 main..038 t1..139 t1..240 main..141 t1..342 main..243 main..344 t1..445 main..446 t1..547 main..548 t1..649 main..650 t1..751 main..752 t1..853 main..854 t1..955 main..956 t1..1057 请按任意键继续. . .58 59 60 */
View Code

 八.Timer定时器

     作用:每隔一段固定的时间执行一段代码

     例:

1 /* 2    定时器 3 */ 4 import java.text.*; 5 import java.util.*; 6 class  Timertest 7 { 8     public static void main(String[] args) throws Exception 9     {10         //创建定时器11         Timer t = new Timer();12         //指定定时任务13         t.schedule(new LogTimerTask(),new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").parse("2017-04-21 11:42:00 000"),10*1000);14 15     }16 }17 //指定任务18 class LogTimerTask extends TimerTask19 {20     public void run()21     {22         System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()));23     }24 }
View Code

 九.线程池

   参考资料   http://www.importnew.com/19011.html

                    http://lavasoft.blog.51cto.com/62575/27069/

                    https://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&mid=2247484346&idx=1&sn=f065ebd404771a90988bb097ea4e26dd&chksm=e9c5fa0bdeb2731d12b838372fc8aa97890dd2e3379d1a486df7cf795f8a1538c1ae974a13e7&mpshare=1&scene=23&srcid=1022Iecc2i96j9Drfxgzd274#rd

转载于:https://www.cnblogs.com/xyzyj/p/6741147.html

你可能感兴趣的文章
Java 知识点
查看>>
Nginx+Tomcat高性能负载均衡集群搭建
查看>>
BZOJ3573: [Hnoi2014]米特运输(树上乱搞)
查看>>
Dubbo的一些编码约定和设计原则
查看>>
IDA 操作记录
查看>>
告警系统需求分析 告警系统主脚本 告警系统配置文件 告警系统监控项目
查看>>
JavaServer Faces (JSF) with Spring
查看>>
知物由学 | 这些企业大佬如何看待2018年的安全形势?
查看>>
[转]Mongodb的下载和安装
查看>>
usb_submit_urb
查看>>
[Node.js] Add Logging to a Node.js Application using Winston
查看>>
qt-solutions提供了8个开源项目
查看>>
处理:“ORA-00257: archiver error. Connect internal only, until freed”的错误问题
查看>>
java 取汉字首字母
查看>>
苹果版小黄车(ofo)app主页菜单效果
查看>>
使用Genymotion模拟器或者手机运行ionic4程序
查看>>
Hadoop之父Doug Cutting
查看>>
关于socket阻塞与非阻塞情况下的recv、send、read、write返回值---部分内容可能不确切,待讨论...
查看>>
Matlab随笔之插值与拟合(上)
查看>>
Socket 通信(基础原理、实时聊天系统雏形)
查看>>