- 浏览: 21714 次
- 性别:
- 来自: 长沙
继上一篇分析了java线程各种状态,以线程终止和线程的调度。现在再来看看最重要的线程安全了,也就是线程同步,下面让我们为多线程当一次锁匠吧。。
在单线程程序中,每次只能做一件事情,后面的事情也需要等待前面的事情完成后才可以进行,如果使用多线程程序,虽然能够实现多处理,但是会发生两个或以上的线程抢占资源的问题,在这个时候就要引进线程安全了。
先看个例子:
public class Test1 implements Runnable{ private int num; public void run(){ for(int i = 0;i<5;i++){ num += i; System.out.println(Thread.currentThread().getName()+"===="+num); try{ Thread.sleep(500); }catch(InterruptedException e){ e.printStackTrace(); } } } public static void main(String [] args){ Test1 t = new Test1(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); t2.start(); } }
结果:
Thread-0====0
Thread-1====0
Thread-0====1
Thread-1====2
Thread-0====4
Thread-1====6
Thread-0====9
Thread-1====12
Thread-0====16
Thread-1====20
看上去好像没什么错,和应该是20啊,但是我们看蓝色是线程0执行的,粉色是线程1执行的,很明显在线程0用了一次num后接着线程1又接着用,而不是等线程0用完了再用,这里就出现了资源了抢占。
但是如果我们把num放进run()方法中就会发现:
public void run(){ int num = 0; for(int i = 0;i<5;i++){ num += i; System.out.println(Thread.currentThread().getName()+"===="+num); try{ Thread.sleep(500); }catch(InterruptedException e){ e.printStackTrace(); } } }
结果:
Thread-0====0
Thread-1====0
Thread-0====1
Thread-1====1
Thread-0====3
Thread-1====3
Thread-0====6
Thread-1====6
Thread-0====10
Thread-1====10
num放进run方法中,就是run方法中的局部变量,每个线程都有这么一个局部变量,每个线程都用自己的num,而不是两个线程共用这么一个num,所以每个线程从0加到4结果都是10。
如何解决资源共享的问题,基本上多有解决多线程资源冲突问题的方法都是在指定时间段内只允许一个线程访问共享资源,这时就需要给共享资源上一道锁,当访问完毕就释放该锁,让别的进程来使用该资源。
实现线程安全的同步机制有:使用synchronized关键字和Lock关键字。下面我来介绍这2种实现线程同步的方式。
1.synchronized:
synchronized可以修饰方法,可以有自己的代码块,也就是修饰对象,synchronized不需要手动释放锁,是自动进行的释放的。还记得我分析过设计模式中的的单例模式,懒汉式的代码中就用到了synchronized关键字来修饰方法。下面用synchronized关键字来解决上例同步问题。
修饰方法:
public synchronized void run(){ for(int i = 0;i<5;i++){ num = num + i; System.out.println(Thread.currentThread().getName()+"===="+num); try{ Thread.sleep(500); }catch(InterruptedException e){ e.printStackTrace(); } } }
也可以加在一般方法上,然后在run()方法中调用这个同步方法,说明这时有多个线程来访问这个方法,但是只允许一个线程访问。
修饰对象:
public void run(){ synchronized(this){ for(int i = 0;i<5;i++){ num = num + i; System.out.println(Thread.currentThread().getName()+"===="+num); try{ Thread.sleep(500); }catch(InterruptedException e){ e.printStackTrace(); } } } }
this代表当前对象,如果使用当前对象作为锁(也可以用其他对象作为锁),那就说明有很多线程需要得到这个对象所,并且执行该对象的方法或者改变该对象的变量。
如果有两个线程试图进入某个类两个类不同方法中,并且同步的是同一个对象锁,那么他们任然是互斥的:
public class ThreadTest1 { public void fun1(){ System.out.println(Thread.currentThread().getName()+":没有同步在fun1()方法中"); synchronized(this){ for(int i = 0;i<3;i++){ System.out.println(Thread.currentThread().getName()+":同步在fun1()方法中"); try{ Thread.sleep(500); }catch(InterruptedException e){ e.printStackTrace(); } } } } public void fun2(){ System.out.println(Thread.currentThread().getName()+":没有同步在fun2()方法中"); synchronized(this){ for(int i = 0;i<3;i++){ System.out.println(Thread.currentThread().getName()+":同步在fun2()方法中"); try{ Thread.sleep(500); }catch(InterruptedException e){ e.printStackTrace(); } } } } public static void main(String [] args){ final ThreadTest1 r = new ThreadTest1(); Thread t1 = new Thread(new Runnable(){ public void run() { r.fun1(); } }); t1.start(); Thread t2 = new Thread(new Runnable(){ public void run() { r.fun2(); } }); t2.start(); } }
结果:
Thread-1:没有同步在fun2()方法中
Thread-1:同步在fun2()方法中
Thread-0:没有同步在fun1()方法中
Thread-1:同步在fun2()方法中
Thread-1:同步在fun2()方法中
Thread-0:同步在fun1()方法中
Thread-0:同步在fun1()方法中
Thread-0:同步在fun1()方法中
可以看出,在一小段时间内,只有一个线程得到同步锁,因而只有一个线程在执行,当线程1执行完线程0才开始执行。
如果两个方法的同步的不是一个对象锁,那会怎样呢?
private Object syncObject = new Object(); public void fun1(){ System.out.println(Thread.currentThread().getName()+":没有同步在fun1()方法中"); synchronized(this){ for(int i = 0;i<3;i++){ System.out.println(Thread.currentThread().getName()+":同步在fun1()方法中"); try{ Thread.sleep(500); }catch(InterruptedException e){ e.printStackTrace(); } } } } public void fun2(){ System.out.println(Thread.currentThread().getName()+":没有同步在fun2()方法中"); synchronized(syncObject){ for(int i = 0;i<3;i++){ System.out.println(Thread.currentThread().getName()+":同步在fun2()方法中"); try{ Thread.sleep(500); }catch(InterruptedException e){ e.printStackTrace(); } } } }
结果:
Thread-1:没有同步在fun2()方法中
Thread-1:同步在fun2()方法中
Thread-0:没有同步在fun1()方法中
Thread-0:同步在fun1()方法中
Thread-1:同步在fun2()方法中
Thread-0:同步在fun1()方法中
Thread-1:同步在fun2()方法中
Thread-0:同步在fun1()方法中
fun1()方法中的同步锁是对象this即当前对象,而fun2()方法中的同步锁是对象syncObject对象,两个方法不是用的同一个对象作为锁,从运行结果看出线程0和线程1是交替进行的,说明都得到了各自所需的锁。
2.Lock:
lock是在方法中,当方法需要锁的时候就lock,当结束的时候就unlock,unlock需要在finally块中。这里需要手动调用unlock方法释放锁。
当用的同一Lock锁:
public class ThreadTest3 { private Lock lock = new ReentrantLock(); public void fun1(){ System.out.println(Thread.currentThread().getName()+":没有同步到fun1()方法中"); lock.lock(); try{ for(int i = 0;i<3;i++){ System.out.println(Thread.currentThread().getName()+":同步到fun1()方法中"); try{ Thread.sleep(500); }catch(InterruptedException e){ e.printStackTrace(); } } }finally{ lock.unlock(); } } public void fun2(){ System.out.println(Thread.currentThread().getName()+":没有同步到fun2()方法中"); lock.lock(); try{ for(int i = 0;i<3;i++){ System.out.println(Thread.currentThread().getName()+":同步到fun2()方法中"); try{ Thread.sleep(500); }catch(InterruptedException e){ e.printStackTrace(); } } }finally{ lock.unlock(); } } public static void main(String [] args){ final ThreadTest3 r = new ThreadTest3(); Thread t1 = new Thread(new Runnable(){ public void run(){ r.fun1(); } }); t1.start(); Thread t2 = new Thread(new Runnable(){ public void run() { r.fun2(); } }); t2.start(); } }
结果:
Thread-0:没有同步到fun1()方法中
Thread-0:同步到fun1()方法中
Thread-1:没有同步到fun2()方法中
Thread-0:同步到fun1()方法中
Thread-0:同步到fun1()方法中
Thread-1:同步到fun2()方法中
Thread-1:同步到fun2()方法中
Thread-1:同步到fun2()方法中
和使用synchronized关键字同步一个对象锁的情况是一样的。
再看看使用不一样的同步锁:
private Lock lock1 = new ReentrantLock(); private Lock lock2 = new ReentrantLock(); public void fun1(){ System.out.println(Thread.currentThread().getName()+":没有同步到fun1()方法中"); lock1.lock(); try{ for(int i = 0;i<3;i++){ System.out.println(Thread.currentThread().getName()+":同步到fun1()方法中"); try{ Thread.sleep(500); }catch(InterruptedException e){ e.printStackTrace(); } } }finally{ lock1.unlock(); } } public void fun2(){ System.out.println(Thread.currentThread().getName()+":没有同步到fun2()方法中"); lock2.lock(); try{ for(int i = 0;i<3;i++){ System.out.println(Thread.currentThread().getName()+":同步到fun2()方法中"); try{ Thread.sleep(500); }catch(InterruptedException e){ e.printStackTrace(); } } }finally{ lock2.unlock(); } }
结果:
Thread-0:没有同步到fun1()方法中
Thread-1:没有同步到fun2()方法中
Thread-0:同步到fun1()方法中
Thread-1:同步到fun2()方法中
Thread-1:同步到fun2()方法中
Thread-0:同步到fun1()方法中
Thread-0:同步到fun1()方法中
Thread-1:同步到fun2()方法中
也可以看出结果和使用synchronized关键字用不同的同步锁一样,线程0和线程1是交替进行的,也都各自得到所需的锁。
synchronized和Lock都可以实现线程的同步,实现资源的共享,每当出现两个甚至多个可以达到同一目的的方式,我们都会问一个问题:哪个更好?这个我没具体测试过,只是在网上搜搜,都是说Lock比synchronized好,但是在同步线程数量少的时候synchronized效率更高,至于这个临界点也不是很清楚。还有一点不一样的就是:如果使用synchronized ,如果A不释放,B将一直等下去,不能被中断。如果使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情。
好吧。我们也为多线程当一次锁匠,以后面对多线程的同步就不会那么棘手了。
发表评论
-
浅谈反射机制性能与安全性
2012-11-23 11:32 2998反射的功能 ... -
初识java反射机制
2012-11-22 17:05 1140什么是java反射机制 ... -
我说java中只有值传递你信么
2012-11-19 20:49 665废话少说,看! public class ... -
java多线程初步理解
2012-11-17 14:52 965也许我们经常做的事就是一边聊QQ一边听音乐, ... -
借助动态绑定实现向上转型
2012-11-13 20:22 838首先介绍一下绑定的概念:绑定指的是一个方法的 ... -
String类有多少你不知道的事
2012-11-12 12:19 690String类是我们再 ...
相关推荐
c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程...
多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程
易语言多线程多次启动一个子程序源码,多线程多次启动一个子程序,循环
多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子
串口通信的实现,编程环境为C#,实现技术采用了多线程方式
C#多线程互斥实例 多线程获取同一变量(不重复)。是一个很好的学习例子
Revit二次开发 c# 本身revit无法进行多线程处理,本实例实现多线程处理
1.使用三种VC的多线程同步方法编写一个多线程的程序(要求在屏幕上先显示Hello,再显示World)。 1)基于全局变量的多线程同步程序; 2)基于事件的多线程同步程序; 3)基于临界区的多线程同步程序。
最近用多线程用的比较多自己走了一些弯路,分享出来希望大家少走弯路,C#中的多线程有两个重载,一个是不带参数的,一个是带参数的,但是即便是带参数的多线程也不支持泛型,这使得使用泛型参数多线程的时候很不方便...
Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式...
先由一个简单的例子引出多线程 先作出这个简单的界面 “开始”对应的槽函数是:slotStart() “停止”对应的槽函数是:slotStop() 本例中的线程(workthread类)实现的功能是,从0到9循环打印,0至9各占一排。 ...
Java多线程读大文件 java多线程写文件:多线程往队列中写入数据
模拟了一个16个摄像机的场景,有不开多线程和打开多线程的对比。 可以明显感觉到打开多线程后主界面不卡了。 只是个多线程的小例子,还有很多不足之处,有待各位完善。 注意:pro文件中的include和lib文件目录需要...
C# 多线程实例C# 多线程实例C# 多线程实例C# 多线程实例C# 多线程实例C# 多线程实例
在计算机处理器发展为包含越来越多的核心的时期,多线程是创建可伸缩性、高效的、高响应性应用程序的关键因素。如果你没有正确地使用多线程,它会导致难以捉摸的问题,需要你花费大量时间去解决。因此,现代应用程序...
C#.NET多线程实例6个(包括多线程基本使用,多线程互斥等全部多线程使用实例),可直接运行
excel vba 多线程 实例 excel vba 多线程 实例
简单多线程编程入门,可以帮助你实现第一个多线程程序
csdn Linux 下的多进程编程初步 ...理上付出了最小的代价,另一方面,又为程序员提供了一个简洁明了的多进程方法。与DOS 和早期的Windows 不同,Unix/Linux 系统是真正实现多任务操作的系统,可以说,不使. . . ./ .
stm32单片机多线程实例