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 */
上述代码的图解如下:
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 */
三.线程的生命周期
线程生命周期状态图:
四.线程的调度与控制
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 */
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 */
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 */
上例原理: 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 */
(另: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 }
六.死锁
例:
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 }
七.守护线程
从线程的分类上可以分为:用户线程和守护线程,所有的用户线程结束生命周期,只有一个用户线程存在,那么守护线程就不会结束,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 */
八.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 }
九.线程池
参考资料 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