
正文
java线程不安全的代码 java线程安全和不安全
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
Java中SimpleDateFormate是线程不安全的又会怎样
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。
线程安全问题都是由全局变量及静态变量引起的。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。
比如一个 ArrayList 类,在添加一个元素的时候,它可能会有两步来完成:1. 在 Items[Size] 的位置存放此元素;2. 增大 Size 的值。
在单线程运行的情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1;
而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 Size 仍然等于 0 (注意哦,我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size 的值。
那好,现在我们来看看 ArrayList 的情况,元素实际上只有一个,存放在位置 0,而 Size 却等于 2。这就是“线程不安全”了。
编辑本段线程安全性类要成为线程安全的,首先必须在单线程环境中有正确的行为。如果一个类实现正确(这是说它符合规格说明的另一种方式),那么没有一种对这个类的对象的操作序列(读或者写公共字段以及调用公共方法)可以让对象处于无效状态,观察到对象处于无效状态、或者违反类的任何不可变量、前置条件或者后置条件的情况。
此外,一个类要成为线程安全的,在被多个线程访问时,不管运行时环境执行这些线程有什么样的时序安排或者交错,它必须仍然有如上所述的正确行为,并且在调用的代码中没有任何额外的同步。其效果就是,在所有线程看来,对于线程安全对象的操作是以固定的、全局一致的顺序发生的。
正确性与线程安全性之间的关系非常类似于在描述 ACID(原子性、一致性、独立性和持久性)事务时使用的一致性与独立性之间的关系:从特定线程的角度看,由不同线程所执行的对象操作是先后(虽然顺序不定)而不是并行执行的。
线程安全性不是一个非真即假的命题。 Vector 的方法都是同步的,并且 Vector 明确地设计为在多线程环境中工作。但是它的线程安全性是有限制的,即在某些方法之间有状态依赖(类似地,如果在迭代过程中 Vector 被其他线程修改,那么由 Vector.iterator() 返回的 iterator会抛出ConcurrentModifiicationException)。
对于 Java 类中常见的线程安全性级别,没有一种分类系统可被广泛接受,不过重要的是在编写类时尽量记录下它们的线程安全行为。
Bloch 给出了描述五类线程安全性的分类方法:不可变、线程安全、有条件线程安全、线程兼容和线程对立。只要明确地记录下线程安全特性,那么您是否使用这种系统都没关系。这种系统有其局限性 -- 各类之间的界线不是百分之百地明确,而且有些情况它没照顾到 -- 但是这套系统是一个很好的起点。这种分类系统的核心是调用者是否可以或者必须用外部同步包围操作(或者一系列操作)。下面几节分别描述了线程安全性的这五种类别。不可变不可变的对象一定是线程安全的,并且永远也不需要额外的同步[1]。因为一个不可变的对象只要构建正确,其外部可见状态永远也不会改变,永远也不会看到它处于不一致的状态。Java 类库中大多数基本数值类如 Integer 、 String 和 BigInteger 都是不可变的。线程安全线程安全的对象具有在上面“线程安全”一节中描述的属性 -- 由类的规格说明所规定的约束在对象被多个线程访问时仍然有效,不管运行时环境如何排列,线程都不需要任何额外的同步。这种线程安全性保证是很严格的 -- 许多类,如 Hashtable 或者 Vector 都不能满足这种严格的定义。有条件的线程安全有条件的线程安全类对于单独的操作可以是线程安全的,但是某些操作序列可能需要外部同步。条件线程安全的最常见的例子是遍历由 Hashtable 或者 Vector 或者返回的迭代器 -- 由这些类返回的 fail-fast 迭代器假定在迭代器进行遍历的时候底层集合不会有变化。为了保证其他线程不会在遍历的时候改变集合,进行迭代的线程应该确保它是独占性地访问集合以实现遍历的完整性。通常,独占性的访问是由对锁的同步保证的 -- 并且类的文档应该说明是哪个锁(通常是对象的内部监视器(intrinsic monitor))。
如果对一个有条件线程安全类进行记录,那么您应该不仅要记录它是有条件线程安全的,而且还要记录必须防止哪些操作序列的并发访问。用户可以合理地假设其他操作序列不需要任何额外的同步。线程兼容线程兼容类不是线程安全的,但是可以通过正确使用同步而在并发环境中安全地使用。这可能意味着用一个 synchronized 块包围每一个方法调用,或者创建一个包装器对象,其中每一个方法都是同步的(就像 Collections.synchronizedList() 一样)。也可能意味着用 synchronized 块包围某些操作序列。为了最大程度地利用线程兼容类,如果所有调用都使用同一个块,那么就不应该要求调用者对该块同步。这样做会使线程兼容的对象作为变量实例包含在其他线程安全的对象中,从而可以利用其所有者对象的同步。
许多常见的类是线程兼容的,如集合类 ArrayList 和 HashMap 、 java.text.SimpleDateFormat 、或者 JDBC 类 Connection 和 ResultSet 。线程对立线程对立类是那些不管是否调用了外部同步都不能在并发使用时安全地呈现的类。线程对立很少见,当类修改静态数据,而静态数据会影响在其他线程中执行的其他类的行为,这时通常会出现线程对立。线程对立类的一个例子是调用 System.setOut() 的类。
相关问答
Q1: java中线程不安全有什么问题
假如一个变量在两个线程中都同时使用,如果一个线程正在操作这个变量的时候突然这个线程进入等待状态,被另一个线程抢了,另一个线程也在操作这个变量,就会发生一些不想出现的结果,所以有些线程要确保安全需要加上锁,同步块什么的
Q2: Java线程安全和非线程安全
ArrayList和Vector有什么区别?HashMap和HashTable有什么区别?StringBuilder和StringBuffer有什么区别?这些都是Java面试中常见的基础问题 面对这样的问题 回答是 ArrayList是非线程安全的 Vector是线程安全的 HashMap是非线程安全的 HashTable是线程安全的 StringBuilder是非线程安全的 StringBuffer是线程安全的 因为这是昨晚刚背的《Java面试题大全》上面写的 此时如果继续问 什么是线程安全?线程安全和非线程安全有什么区别?分别在什么情况下使用?这样一连串的问题 一口老血就喷出来了…
非线程安全的现象模拟
这里就使用ArrayList和Vector二者来说明
下面的代码 在主线程中new了一个非线程安全的ArrayList 然后开 个线程分别向这个ArrayList里面添加元素 每个线程添加 个元素 等所有线程执行完成后 这个ArrayList的size应该是多少?应该是 个?
[java]
public class Main
{
public static void main(String[] args)
{
// 进行 次测试
for(int i = ; i ; i++)
{
test();
}
}
public static void test()
{
// 用来测试的List
ListObject list = new ArrayListObject();
// 线程数量( )
int threadCount = ;
// 用来让主线程等待threadCount个子线程执行完毕
CountDownLatch countDownLatch = new CountDownLatch(threadCount);
// 启动threadCount个子线程
for(int i = ; i threadCount; i++)
{
Thread thread = new Thread(new MyThread(list countDownLatch));
thread start();
}
try
{
// 主线程等待所有子线程执行完成 再向下执行
countDownLatch await();
}
catch (InterruptedException e)
{
e printStackTrace();
}
// List的size
System out println(list size());
}
}
class MyThread implements Runnable
{
private ListObject list;
private CountDownLatch countDownLatch;
public MyThread(ListObject list CountDownLatch countDownLatch)
{
this list = list;
untDownLatch = countDownLatch;
}
public void run()
{
// 每个线程向List中添加 个元素
for(int i = ; i ; i++)
{
list add(new Object());
}
// 完成一个子线程
untDown();
}
}
public class Main
{
public static void main(String[] args)
{
// 进行 次测试
for(int i = ; i ; i++)
{
test();
}
}
public static void test()
{
// 用来测试的List
ListObject list = new ArrayListObject();
// 线程数量( )
int threadCount = ;
// 用来让主线程等待threadCount个子线程执行完毕
CountDownLatch countDownLatch = new CountDownLatch(threadCount);
// 启动threadCount个子线程
for(int i = ; i threadCount; i++)
{
Thread thread = new Thread(new MyThread(list countDownLatch));
thread start();
}
try
{
// 主线程等待所有子线程执行完成 再向下执行
countDownLatch await();
}
catch (InterruptedException e)
{
e printStackTrace();
}
// List的size
System out println(list size());
}
}
class MyThread implements Runnable
{
private ListObject list;
private CountDownLatch countDownLatch;
public MyThread(ListObject list CountDownLatch countDownLatch)
{
this list = list;
untDownLatch = countDownLatch;
}
public void run()
{
// 每个线程向List中添加 个元素
for(int i = ; i ; i++)
{
list add(new Object());
}
// 完成一个子线程
untDown();
}
}
上面进行了 次测试(为什么要测试 次?因为非线程安全并不是每次都会导致问题)
输出结果
上面的输出结果发现 并不是每次测试结果都是 有好几次测试最后ArrayList的size小于 甚至时不时会抛出个IndexOutOfBoundsException异常 (如果没有这个现象可以多试几次)
这就是非线程安全带来的问题了 上面的代码如果用于生产环境 就会有隐患就会有BUG了
再用线程安全的Vector来进行测试 上面代码改变一处 test()方法中
[java]
ListObject list = new ArrayListObject();
ListObject list = new ArrayListObject();改成
[java]
ListObject list = new VectorObject();
ListObject list = new VectorObject();
再运行程序
输出结果
再多跑几次 发现都是 没有任何问题 因为Vector是线程安全的 在多线程操作同一个Vector对象时 不会有任何问题
再换成LinkedList试试 同样还会出现ArrayList类似的问题 因为LinkedList也是非线程安全的
二者如何取舍
非线程安全是指多线程操作同一个对象可能会出现问题 而线程安全则是多线程操作同一个对象不会有问题
线程安全必须要使用很多synchronized关键字来同步控制 所以必然会导致性能的降低
所以在使用的时候 如果是多个线程操作同一个对象 那么使用线程安全的Vector 否则 就使用效率更高的ArrayList
非线程安全!=不安全
有人在使用过程中有一个不正确的观点 我的程序是多线程的 不能使用ArrayList要使用Vector 这样才安全
非线程安全并不是多线程环境下就不能使用 注意我上面有说到 多线程操作同一个对象 注意是同一个对象 比如最上面那个模拟 就是在主线程中new的一个ArrayList然后多个线程操作同一个ArrayList对象
如果是每个线程中new一个ArrayList 而这个ArrayList只在这一个线程中使用 那么肯定是没问题的
线程安全的实现
线程安全是通过线程同步控制来实现的 也就是synchronized关键字
在这里 我用代码分别实现了一个非线程安全的计数器和线程安全的计数器Counter 并对他们分别进行了多线程测试
非线程安全的计数器
[java]
public class Main
{
public static void main(String[] args)
{
// 进行 次测试
for(int i = ; i ; i++)
{
test();
}
}
public static void test()
{
// 计数器
Counter counter = new Counter();
// 线程数量( )
int threadCount = ;
// 用来让主线程等待threadCount个子线程执行完毕
CountDownLatch countDownLatch = new CountDownLatch(threadCount);
// 启动threadCount个子线程
for(int i = ; i threadCount; i++)
{
Thread thread = new Thread(new MyThread(counter countDownLatch));
thread start();
}
try
{
// 主线程等待所有子线程执行完成 再向下执行
countDownLatch await();
}
catch (InterruptedException e)
{
e printStackTrace();
}
// 计数器的值
System out println(counter getCount());
}
}
class MyThread implements Runnable
{
private Counter counter;
private CountDownLatch countDownLatch;
public MyThread(Counter counter CountDownLatch countDownLatch)
{
unter = counter;
untDownLatch = countDownLatch;
}
public void run()
{
// 每个线程向Counter中进行 次累加
for(int i = ; i ; i++)
{
counter addCount();
}
// 完成一个子线程
untDown();
}
}
class Counter
{
private int count = ;
public int getCount()
{
return count;
}
public void addCount()
{
count++;
}
}
public class Main
{
public static void main(String[] args)
{
// 进行 次测试
for(int i = ; i ; i++)
{
test();
}
}
public static void test()
{
// 计数器
Counter counter = new Counter();
// 线程数量( )
int threadCount = ;
// 用来让主线程等待threadCount个子线程执行完毕
CountDownLatch countDownLatch = new CountDownLatch(threadCount);
// 启动threadCount个子线程
for(int i = ; i threadCount; i++)
{
Thread thread = new Thread(new MyThread(counter countDownLatch));
thread start();
}
try
{
// 主线程等待所有子线程执行完成 再向下执行
countDownLatch await();
}
catch (InterruptedException e)
{
e printStackTrace();
}
// 计数器的值
System out println(counter getCount());
}
}
class MyThread implements Runnable
{
private Counter counter;
private CountDownLatch countDownLatch;
public MyThread(Counter counter CountDownLatch countDownLatch)
{
unter = counter;
untDownLatch = countDownLatch;
}
public void run()
{
// 每个线程向Counter中进行 次累加
for(int i = ; i ; i++)
{
counter addCount();
}
// 完成一个子线程
untDown();
}
}
class Counter
{
private int count = ;
public int getCount()
{
return count;
}
public void addCount()
{
count++;
}
}
上面的测试代码中 开启 个线程 每个线程对计数器进行 次累加 最终输出结果应该是
但是上面代码中的Counter未进行同步控制 所以非线程安全
输出结果
稍加修改 把Counter改成线程安全的计数器
[java]
class Counter
{
private int count = ;
public int getCount()
{
return count;
}
public synchronized void addCount()
{
count++;
}
}
class Counter
{
private int count = ;
public int getCount()
{
return count;
}
public synchronized void addCount()
{
count++;
}
}
上面只是在addCount()方法中加上了synchronized同步控制 就成为一个线程安全的计数器了 再执行程序
输出结果
lishixinzhi/Article/program/Java/gj/201311/27519
Q3: JAVA中的HashMap是线程非安全的,谁能举个实际代码测试的例子,证明HashMap的确是线程非安全的
这个不要代码测试的例子,随便写个多线程,你用HashMap一个get操作,一个put操作,只要这两个操作是同时进行的,不就产生线程的不安全了吗?
Q4: java中什么是线程不安全给出一个例子
两个线程对一个变量进行操作java线程不安全的代码,但是没有上锁java线程不安全的代码,即没有进行同步操作java线程不安全的代码,就像买车票java线程不安全的代码的时候多个窗口一起卖java线程不安全的代码,但是车票数没有减去。车票被重复卖出。就是不安全。还有,被多个线程操作的变量改为静态量加static,才安全
关于java线程不安全的代码和java线程安全和不安全的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。






