线程安全也可是说是并发安全。
在多线程环境下能正确执行的代码就是线程安全的代码。安全的意思就是说能正确执行,否则后面就是程序执行错误或者出现各种异常情况。
线程安全是指多线程访问同一代码或者同一共享数据时,不会产生不确定的结果。编写线程安全的代码依靠的是线程同步。
线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据
非线程安全是指多线程操作同一个对象可能会出现问题,而线程安全则是多线程操作同一个对象不会出现问题。
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。线程安全问题都是由全局变量及静态变量引起的。其实准确的说应该是共享变量引起的。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。线程安全问题主要出现在访问临界资源的时候,就是访问同一个对象的时候,可能会出现无法挽回的损失,特别是在关于资金安全方面的时候,当然还有数据库事务方面的问题。他们很类似,都是要保证数据的原子性。那么在java中如何保证线程安全呢?对与共同使用的对象进行加锁,意思是使用的时候,那么你就必须等待,等我用完之后你再用,反之依然。就像上厕所,你去的时候我是不能去的。
下面看一个线程非安全具体实例:
/**
* 非线程安全举例
* @author zhou
*
*/
public class ThreadSecurityTest {
//Outerput outerput2 = new Outerput(); is ok
public static void main(String[] args) {
new ThreadSecurityTest().sartThread();
}
public void sartThread(){
final Outerput outerput = new Outerput();
new Thread(new Runnable(){
@Override
public void run() {
while(true){
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
/* synchronized(outerput){
outerput.print("zhangsan");
}*/
new Outerput().print("zhangsan");
}
}
}).start();
new Thread(new Runnable(){
@Override
public void run() {
while(true){
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
/* synchronized (outerput) {
outerput.print("lisi");
}*/
new Outerput().print("lisi");
}
}
}).start();
}
public class Outerput{
/* public synchronized void print(String name){ //is ok
for(int i = 0;i < name.length(); i++){
System.out.print(name.charAt(i));
printToFile(name.charAt(i));
}
printToFile('\r');
printToFile('\n');
System.out.println();
}*/
public void print(String name){
for(int i = 0;i < name.length(); i++){
System.out.print(name.charAt(i));
printToFile(name.charAt(i));
}
printToFile('\r');
printToFile('\n');
System.out.println();
}
public synchronized void printToFile(char ch) {
try {
File file = new File("outputChar_ok.txt");
// if(!file.exists()){
FileWriter fw = new FileWriter(file, true);
FileOutputStream fos = new FileOutputStream("outputChar2_ok.txt", true);
fos.write(ch);
fos.flush();
// fw.write(ch);
fw.append(ch);
fw.flush();
fw.close();
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
输出的结果中可能出现这种情况:
lizshiang
san
lisi
zhangsan
lisi
zhangsan
lisi
zhangsalnisi
...
原因:以上代码没有对共同持有的对象outerput加锁,所以会出现线程安全问题
1、对代码块加锁
对共同持有的对象加锁可以把内部类写成这样的
1. public class Outerput{
2. public void print(String name){
3. synchronized (this) {
4. for(int i = 0;i < name.length(); i++){
5. System.out.print(name.charAt(i));
6. }
7. System.out.println();
8. }
9. }
10. }
2、对非静态方法加锁,加锁的对象是this
1. public class Outerput{
2. public synchronized void print(String name){
3. for(int i = 0;i < name.length(); i++){
4. System.out.print(name.charAt(i));
5. }
6. System.out.println();
7. }
8. }
3、对静态方法加锁的对象到底是谁?
1. public static synchronized void print2(String name){
2. for(int i = 0;i < name.length(); i++){
3. System.out.print(name.charAt(i));
4. }
5. System.out.println();
6. }
4、在线程中的run方法中进行处理加锁 如
synchronized (outerput) {
outerput.print("lisi");
}
其实加锁的对象是字节码对象,Outerput.class
如果和非静态方法同时持有同一个对象时,可以持有同一个字节码对象。