1 /* 2 t1和t2 3 4 异步编程模型:t1线程执行t1的,t2线程执行t2的,两个线程之间谁也不等谁。 5 6 同步编程模型:t1线程和t2线程执行,当t1线程必须等t2线程执行结束之后,t1线程才能执行,这是同步编程模型。 7 8 9 什么时候要同步呢?为什么要引入线程同步呢? 10 1.为了数据的安全。尽管应用程序的使用率降低,但是为了保证数据是安全的,必须加入线程同步机制。 11 线程同步机制使程序变成了(等同)单线程。 12 13 2.什么条件下要使用线程同步? 14 第一:必须是多线程环境 15 第二:多线程环境共享同一个数据. 16 第三:共享的数据涉及到修改操作。 17 18 以下程序演示取款例子。以下程序不使用线程同步机制,多线程 19 同时对同一个账户进行取款操作,会出现什么问题? 20 */ 21 public class ThreadTest12 22 { 23 public static void main(String[] args){ 24 25 //创建一个公共的账户 26 Account act = new Account("actno-001",5000.0); 27 28 //创建线程对同一个账户取款 29 Thread t1 = new Thread(new Processor(act)); 30 Thread t2 = new Thread(new Processor(act)); 31 32 t1.start(); 33 34 t2.start(); 35 } 36 } 37 38 //取款线程 39 class Processor implements Runnable 40 { 41 //账户 42 Account act; 43 44 //Constructor 45 Processor(Account act){ 46 this.act = act; 47 } 48 49 public void run(){ 50 act.withdraw(1000.0); 51 System.out.println("取款1000.0成功,余额:" + act.getBalance()); 52 } 53 } 54 55 //账户 56 class Account 57 { 58 private String actno; 59 private double balance; 60 61 public Account(){} 62 public Account(String actno,double balance){ 63 this.actno = actno; 64 this.balance = balance; 65 } 66 67 //setter and getter 68 public void setActno(String actno){ 69 this.actno = actno; 70 } 71 72 public void setBalance(double balance){ 73 this.balance = balance; 74 } 75 76 public String getActno(){ 77 return actno; 78 } 79 80 public double getBalance(){ 81 return balance; 82 } 83 84 //对外提供一个取款的方法 85 public void withdraw(double money){ //对当前账户进行取款操作 86 87 double after = balance - money; 88 89 //延迟 90 try{Thread.sleep(1000);}catch(Exception e){} 91 92 93 //更新 94 this.setBalance(after); 95 } 96 }
// 以上程序输出结果 /* ---------- java ---------- 取款1000.0成功,余额:4000.0 取款1000.0成功,余额:4000.0 Output completed (1 sec consumed) - Normal Termination */
由上面的结果可知在第一个线程更新余额之前,第二个线程访问了账户余额,才导致了上面的结果,利用Java的synchronized关键字,我们改进的程序如下:
1 /* 2 以下程序使用线程同步机制保证数据的安全。 3 */ 4 public class ThreadTest13 5 { 6 public static void main(String[] args){ 7 8 //创建一个公共的账户 9 Account act = new Account("actno-001",5000.0); 10 11 //创建线程对同一个账户取款 12 Thread t1 = new Thread(new Processor(act)); 13 Thread t2 = new Thread(new Processor(act)); 14 15 t1.start(); 16 17 t2.start(); 18 } 19 } 20 21 //取款线程 22 class Processor implements Runnable 23 { 24 //账户 25 Account act; 26 27 //Constructor 28 Processor(Account act){ 29 this.act = act; 30 } 31 32 public void run(){ 33 act.withdraw(1000.0); 34 System.out.println("取款1000.0成功,余额:" + act.getBalance()); 35 } 36 } 37 38 //账户 39 class Account 40 { 41 private String actno; 42 private double balance; 43 44 public Account(){} 45 public Account(String actno,double balance){ 46 this.actno = actno; 47 this.balance = balance; 48 } 49 50 //setter and getter 51 public void setActno(String actno){ 52 this.actno = actno; 53 } 54 55 public void setBalance(double balance){ 56 this.balance = balance; 57 } 58 59 public String getActno(){ 60 return actno; 61 } 62 63 public double getBalance(){ 64 return balance; 65 } 66 67 //对外提供一个取款的方法 68 public void withdraw(double money){ //对当前账户进行取款操作 69 70 //把需要同步的代码,放到同步语句块中. 71 /* 72 原理:t1线程和t2线程. 73 t1线程执行到此处,遇到了synchronized关键字,就会去找this的对象锁, 74 如果找到this对象锁,则进入同步语句块中执行程序。当同步语句块中的代码 75 执行结束之后,t1线程归还this的对象锁。 76 77 在t1线程执行同步语句块的过程中,如果t2线程也过来执行以下代码,也遇到 78 synchronized关键字,所以也去找this的对象锁,但是该对象锁被t1线程持有, 79 只能在这等待this对象的归还。 80 */ 81 synchronized(this){ 82 83 double after = balance - money; 84 85 //延迟 86 try{Thread.sleep(1000);}catch(Exception e){} 87 88 89 //更新 90 this.setBalance(after); 91 } 92 93 } 94 }
// 运行结果: /* ---------- java ---------- 取款1000.0成功,余额:4000.0 取款1000.0成功,余额:3000.0 Output completed (2 sec consumed) - Normal Termination */
sychronized修饰静态代码块或者静态方法,产生类锁,每个类共享一把锁
sychronized修饰非静态代码块或者成员方法,产生对象锁,每个对象共享一把锁
死锁
以下程序描述了死锁
1 /* 2 死锁 3 */ 4 public class DeadLock 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 14 t1.start(); 15 t2.start(); 16 17 } 18 } 19 20 class T1 implements Runnable 21 { 22 Object o1; 23 Object o2; 24 25 T1(Object o1,Object o2){ 26 this.o1 = o1; 27 this.o2 = o2; 28 } 29 30 public void run(){ 31 synchronized(o1){ 32 try{Thread.sleep(1000);}catch(Exception e){} 33 synchronized(o2){ 34 35 } 36 } 37 } 38 } 39 40 41 class T2 implements Runnable 42 { 43 Object o1; 44 Object o2; 45 46 T2(Object o1,Object o2){ 47 this.o1 = o1; 48 this.o2 = o2; 49 } 50 51 public void run(){ 52 synchronized(o2){ 53 try{Thread.sleep(1000);}catch(Exception e){} 54 synchronized(o1){ 55 56 } 57 } 58 } 59 }
以上程序将永远等待执行,相似的概念是“羊群效应”,“哲学家吃饭问题”
守护线程
从线程分类上可以分为:用户线程(以上讲的都是用户线程),另一个是守护线程。守护线程是这样的,所有的用户线程结束生命周期,守护线程才会结束生命周期,只要有一个用户线程存在,那么守护线程就不会结束,例如 java 中著名的垃圾回收器就是一个守护线程,只有应用程序中所有的线程结束,它才会结束。
1 /* 2 守护线程. 3 4 其他所有的用户线程结束,则守护线程退出! 5 守护线程一般都是无限执行的. 6 */ 7 public class ThreadTest19 8 { 9 public static void main(String[] args) throws Exception{ 10 11 Thread t1 = new Processor(); 12 13 t1.setName("t1"); 14 15 //将t1这个用户线程修改成守护线程. 16 t1.setDaemon(true); 17 18 t1.start(); 19 20 //主线程 21 for(int i=0;i<10;i++){ 22 System.out.println(Thread.currentThread().getName()+"-->"+i); 23 Thread.sleep(1000); 24 } 25 } 26 } 27 28 class Processor extends Thread 29 { 30 public void run(){ 31 int i = 0; 32 while(true){ 33 34 i++; 35 36 System.out.println(Thread.currentThread().getName()+"-->"+i); 37 38 try{Thread.sleep(500);}catch(Exception e){} 39 40 } 41 42 } 43 }
// 输出结果 /* ---------- java ---------- main-->0 t1-->1 t1-->2 main-->1 t1-->3 t1-->4 main-->2 t1-->5 t1-->6 main-->3 t1-->7 t1-->8 main-->4 t1-->9 t1-->10 main-->5 t1-->11 t1-->12 main-->6 t1-->13 t1-->14 main-->7 t1-->15 t1-->16 main-->8 t1-->17 t1-->18 main-->9 t1-->19 t1-->20 Output completed (10 sec consumed) - Normal Termination */
主线程输出结束之后 t 线程结束
定时器
1 /* 2 关于定时器的应用. 3 作用:每隔一段固定的时间执行一段代码. 4 */ 5 import java.text.*; 6 import java.util.*; 7 8 public class TimerTest01 9 { 10 public static void main(String[] args) throws Exception{ 11 12 //1.创建定时器 13 Timer t = new Timer(); 14 15 //2.指定定时任务 16 t.schedule( 17 new LogTimerTask(), 18 new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").parse("2012-08-03 17:37:00 000"), 19 1000*10); 20 21 } 22 } 23 24 //指定任务 25 class LogTimerTask extends TimerTask 26 { 27 public void run(){ 28 System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date())); 29 } 30 }
注意:java.lang.Object下有两个方法分别是notify和wait,是控制当前对象线程等待和继续的方法
从线程分类上可以分为:用户线程(以上讲的都是用户线程),另一个是守护线程。守护线栈 堆 方法区main{……..}run {………}t1 线程run{i=0..i<10}t2 线程run {i=0..i<10}Processors = 0s = 45Processors = 0s = 45动力节点 http://www.bjpowernode.com17程是这样的,所有的用户线程结束生命周期,守护线程才会结束生命周期,只要有一个用户线程存在,那么守护线程就不会结束,例如 java 中著名的垃圾回收器就是一个守护线程,只有应用程序中所有的线程结束,它才会结束。