java中创建线程的三种方法
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 Thread 24 { 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.....0 37 main.....1 38 main.....2 39 run....0 40 main.....3 41 run....1 42 main.....4 43 run....2 44 main.....5 45 run....3 46 main.....6 47 run....4 48 main.....7 49 run....5 50 main.....8 51 run....6 52 */
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 Runnable 18 { 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...8 28 mian...10 29 run...9 30 mian...11 31 run...10 32 mian...12 33 run...11 34 mian...13 35 run...12 36 */
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()); //默认优先级为5 19 System.out.println(t2.getPriority()); //默认优先级为5 20 //设置优先级 21 t1.setPriority(4); 22 t2.setPriority(9); 23 24 System.out.println(t1.getPriority()); //默认优先级为5 25 System.out.println(t2.getPriority()); //默认优先级为5 26 //启动线程 27 t1.start(); 28 t2.start(); 29 } 30 } 31 32 class P extends Thread 33 { 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 10 45 1 46 5 47 5 48 5 49 4 50 9 51 t1...0 52 t2...0 53 t1...1 54 t2...1 55 t1...2 56 t2...2 57 t1...3 58 t2...3 59 t1...4 60 t2...4 61 t1...5 62 t2...5 63 t1...6 64 t2...6 65 t1...7 66 t2...7 67 t1...8 68 t2...8 69 t1...9 70 t2...9 71 请按任意键继续. . . 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 Thread 24 { 25 //被重写的方法不能抛出异常,在run方法的声明为置上不能呢使用Throws, 26 public void run() 27 { 28 for(int i = 0;i<10;i++) 29 { 30 31 try{ 32 Thread.sleep(1000);//让当前线程阻塞1s 33 }catch(InterruptedException e) 34 { 35 e.printStackTrace(); 36 } 37 System.out.println(Thread.currentThread().getName()+".."+i); 38 } 39 } 40 } 41 /* 42 运行结果: 43 main..0 44 t1..0 45 main..1 46 t1..1 47 main..2 48 main..3 49 main..4 50 t1..2 51 main..5 52 t1..3 53 main..6 54 main..7 55 t1..4 56 main..8 57 main..9 58 t1..5 59 t1..6 60 t1..7 61 t1..8 62 t1..9 63 请按任意键继续. . . 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 Runnable 19 { 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 Account 35 { 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.0 75 取款成功,余额为:6000.0 76 请按任意键继续. . . 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 Runnable 19 { 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 Account 35 { 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.0 75 取款成功,余额为:6000.0 76 请按任意键继续. . . 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 Runnable 19 { 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 MyClass 30 { 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 Runnable 19 { 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 Runnable 40 { 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 Thread 23 { 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..0 38 t1..1 39 t1..2 40 main..1 41 t1..3 42 main..2 43 main..3 44 t1..4 45 main..4 46 t1..5 47 main..5 48 t1..6 49 main..6 50 t1..7 51 main..7 52 t1..8 53 main..8 54 t1..9 55 main..9 56 t1..10 57 请按任意键继续. . . 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 TimerTask 19 { 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
,
Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。Java可以用三种方式来创建线程,如下所示:
1)继承Thread类创建线程
2)实现Runnable接口创建线程
3)使用Callable和Future创建线程