说说final

final 这个关键字从学Java开始就有提及。它可以用来修饰class, method, method parameter, variable等。这里主要说说最后这一种。

对于有final 修饰的variable, 无论是instance variable 还是local variable, 其含义是说(一般)一经初始化和赋值,其值就无法改变。这里,对于instance variable, 我们说,这“初始化和赋值”可能是在声明时就完成的,也可以是在构造函数中完成的。

然而,这个一般无法改变的值却是可以改变的。在JSR 133 中提到了用反射改变final 的instance field 的方法。这里截取的是那里面的例子:

class  A  ... {
final int x;
A() 
...{
= 1;
}

int f() ...{
return d(this,this);
}

int d(A a1, A a2) ...{
int i = a1.x;
g(a1);
int j = a2.x;
return j - i;
}

static void g(A a) ...{
// uses reflection to change a.x to 2
}

}

在JDK1.4 以及它以前的版本中,这种方法是行不通的。而在JDK 5 中,这种方法却可行的。具体做法是分别调用Field 的setAccessible(true) 方法和setX 方法进行赋值。

但是,这种方法可能会因为reorder 导致问题。JSR133 中说到:

... Another problem is that the semantics is designed to allow aggressive optimization of final fields.
Within a thread, it is permissible to reorder reads of a final field with calls to methods that may
change final fields via reflection.

所以,在JSR 133 中,提出了Final Field Safe Context 的概念。这个名词的意思是,在这个context 中,可以保证不会发生reorder 的现象。但奇怪的是,现在JVM 的实现中,似乎没有为这个语义定义关键字。

 

说到final, 顺便说一下static final. static final 在初始化以后,几乎都不能改变。这里说“几乎” 的原因是有例外:

Static final fields may only be modified in the class initializer that defines them, with the exception
of the java.lang.System.in, java.lang.System.out, and java.lang.System.err static fields, which can be modified by the java.lang.System.setIn, java.lang.System.setOut, and java.lang.System.setErr methods.

另外,顺便说一下,对于有无static 修饰的final field, 其在JVM 内部的初始化函数是不同的。这个有兴趣的可以看一下后面的参考文档。

 

最后要说的是所谓值“不能改变”的意思。概括的说,对于primitive 类型的变量,一旦声明为final, 除了上面的情况,初始化后就不能改了。而对于对象类型的field, “不能改变”的是它的引用。要理解这句话的意思,请看一下这段代码:

package  com.dproxy;

import  java.util.ArrayList;

public   class  FinalChangeTest {
    
public   final   int  a  =   3 ;
    
    
public   final  Object b  =   null ;
    
    
public   final  String c  =   " abc " ;
    
    
public   final  StringBuffer d  =   new  StringBuffer( " a " );
    
    
public   final  ArrayList e  =   new  ArrayList();
    
    
public   final  TestClass f  =   new  TestClass();
    
    
public   final   int [] g  =   new   int [ 3 ];
    
    
public   static   void  main(String args[]){
        FinalChangeTest test 
=   new  FinalChangeTest ();
        
//         test.a = 4;  //  error
//         test.b = "";  // error
        
//         test.c = "";  // error
        
//         test.d = test.d.append("ttt");  // error
        test.d.setCharAt( 0 ' c ' );
        
        test.e.add(
"" );
    
//         System.out.println(test.f.i);  // 1
        test.f.i  =   2 ;
//         System.out.println(test.f.i);  // 2
        
        test.g[
0 =   3 ;
    }
    
    
class  TestClass{
        
public   int  i  =   1 ;
    }
    
    
public   void  finalVarTest(){
        
final   int  a  =   3 ;
        
        
final  Object b  =   null ;
        
        
final  String c  =   " abc " ;
        
        
final  StringBuffer d  =   new  StringBuffer( " a " );
        
        
final  ArrayList e  =   new  ArrayList();
        
        
final  TestClass f  =   new  TestClass();
        
        
final   int [] g  =   new   int [ 3 ];
        
//         a=2;     // error
//         b=""; // error
//         c=""; // error
        
        d.setCharAt(
0 ' c ' );
        
        e.add(
"" );
        
        f.i 
=   0 ;
        
        g[
1 =   2 ;
        
            
    }
}

上面代码里注释部分的句子都是无法通过编译的。对比这个,读者可以理解一下。

 

参考:

JSR133

http://www.cs.umd.edu/~pugh/java/memoryModel/archive/2398.html

http://dev2dev.bea.com.cn/bbsdoc/20060704286.html

 

 

 

 

 

 

 

 

### Java 中 Lock 的作用 在 Java 并发编程中,`Lock` 是一个接口,定义在 `java.util.concurrent.locks` 包中,用于提供比 `synchronized` 更灵活和强大的线程同步机制。与 `synchronized` 不同,`Lock` 允许尝试获取锁、超时获取锁以及在获取锁失败时进行自定义处理。它还支持尝试非阻塞地获取锁,从而避免死锁[^1]。 `Lock` 的实现类中最常用的是 `ReentrantLock`,它提供了与 `synchronized` 类似的可重入特性,但增加了更多的控制选项,例如公平锁与非公平锁的选择。这种灵活性使得 `ReentrantLock` 在复杂的并发场景中更具优势。 ```java import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockExample { private final Lock lock = new ReentrantLock(); private int count = 0; public void increment() { lock.lock(); try { count++; } finally { lock.unlock(); } } public static void main(String[] args) throws InterruptedException { LockExample example = new LockExample(); Thread t1 = new Thread(() -> { for (int i = 0; i < 1000; i++) { example.increment(); } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 1000; i++) { example.increment(); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("Final count: " + example.count); } } ``` ### Lock 与 Synchronized 的区别 1. **锁的获取与释放方式** `synchronized` 是隐式锁,进入同步代码块时自动获取锁,退出代码块时自动释放锁;而 `Lock` 是显式锁,需要手动调用 `lock()` 和 `unlock()` 方法来获取和释放锁。这种方式虽然增加了代码复杂度,但也提高了灵活性和控制能力。 2. **尝试获取锁与超时机制** `Lock` 提供了 `tryLock()` 方法,允许线程尝试获取锁而不阻塞。它还可以指定等待锁的超时时间,适用于需要避免死锁的场景。`synchronized` 则无法实现这种非阻塞式获取锁的机制。 3. **公平性支持** `ReentrantLock` 允许选择公平锁或非公平锁。公平锁按照线程请求锁的顺序进行分配,而非公平锁则允许插队,适用于对性能要求较高的场景。`synchronized` 只支持非公平锁,不保证线程获取锁的顺序。 4. **锁升级与优化机制** `synchronized` 在 JDK 1.6 及之后引入了偏向锁、轻量级锁等优化机制,能够根据线程竞争情况自动升级锁状态,减少线程阻塞和上下文切换带来的性能损耗。`Lock` 没有内置的锁升级机制,但其显式控制特性使得开发者可以根据具体需求优化锁的使用方式。 5. **性能表现** 在低竞争场景下,`synchronized` 的优化机制使其性能表现良好;而在高竞争场景下,`ReentrantLock` 由于提供了更多控制选项,通常能够获得更好的性能。然而,`ReentrantLock` 的显式锁管理也带来了额外的开发和维护成本。 6. **条件变量支持** `ReentrantLock` 支持多个条件变量(`Condition`),允许线程在不同条件下等待和唤醒,适用于复杂的同步需求。`synchronized` 仅支持单一的等待/通知机制,无法满足多条件变量的场景。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值