多线程编程并发解决之道-线程锁技术

时间:2022-06-15 15:09:55

实际项目中多线程技术的应用十分的广泛,但如果程序中涉及到多个线程对同一变量的写操作,那么就会涉及到线程安全的问题,如何解决多个线程对同一数据的并发操作呢?毫无疑问采用加锁的方式解决线程并发问题是目前解决多线程并发问题的主要技术,下面来介绍一下java传统加锁方式和java5的线程锁技术。

在java传统的加锁技术中synchronized是实现线程互斥的关键,根据加锁粒度的粗细,该关键字可以加在方法级别上,也可以加在代码块级别上,根据业务需要可以自定义加锁范围,它的实现代码可以是这样的:


public class TranditionalThreadSynchronized {

public static void main(String[] args){
new TranditionalThreadSynchronized().init();
}

private void init(){
final Outputer outputer = new Outputer();
new Thread(new Runnable(){
@Override
public void run() {
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
outputer.output("zhangsan");

}

}
}).start();


new Thread(new Runnable(){
@Override
public void run() {
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
outputer.output("lisi");

}

}
}).start();
}
class Outputer{
public void output(String name){
int len = name.length();
synchronized(this){
for(int i=0;i<len;i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
}
简单分析一下代码可以在init方法中开启了两个线程,这两个线程拥有对同一个Outputer对象的引用,也正是因为这个原因所以才产生了线程并发的问题,如果他们所持有的是两个不同的Outputer对象则不会出现并发问题。从代码中可以看出传统的线程互斥技术通过对代码块进行加锁来保持数据的安全性。针对java这种纯面向对象的语言结合现实生活中的真实场景锁更应该通过对象来体现,所以在java5中的所对象应运而生。下面改造一下代码,来看看java5是如何通过面向对象的思想保证线程安全的:


public class LockTest {

public static void main(String[] args){
new LockTest().init();
}

private void init(){
final Outputer outputer = new Outputer();
new Thread(new Runnable(){
@Override
public void run() {
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
outputer.output("zhangxiaoxiang");

}

}
}).start();


new Thread(new Runnable(){
@Override
public void run() {
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
outputer.output("lihuoming");

}

}
}).start();
}
class Outputer{
Lock lock = new ReentrantLock();
public void output(String name){
int len = name.length();
//synchronized(this){
lock.lock();
try{
for(int i=0;i<len;i++){
System.out.print(name.charAt(i));
}
System.out.println();
}finally{
lock.unlock();
}

//}
}
}
}
对比这两种加锁方式,可以简单的归纳一下在传统的加锁方式中,是通过外部加锁实现的也就是锁这个概念并不具体的隶属于执行对象,它是一把公用所,任何对象都可以使用这把公用锁来锁住需要做并发处理的代码,而java5中的线程锁技术明显更加面向对象化了,每一个执行对象都拥有一把属于自己的锁,用自己的锁给需要进行并发处理的代码进行加锁和解锁操作,但无论是哪种方式加锁的思想是不变的,如果两个线程需要需要做互斥操作那么他们必须要用同一把锁才能达到互斥效果。