首页编程java编程java什么是同步集合,java类内多个函数如何同步

java什么是同步集合,java类内多个函数如何同步

编程之家2023-10-1194次浏览

大家好,关于java什么是同步集合很多朋友都还不太明白,今天小编就来为大家分享关于java类内多个函数如何同步的知识,希望对各位有所帮助!

java什么是同步集合,java类内多个函数如何同步

java中Arraylist是干什么的怎么用

java中的ArrayList就是传说中的动态数组,用MSDN中的说法,就是Array的复杂版本。

它提供了如下一些好处:动态的增加和减少元素实现了ICollection和IList接口灵活的设置数组的大小。

ArrayList的用法:

java什么是同步集合,java类内多个函数如何同步

ArrayList List= new ArrayList(); for( int

i=0;i<10;i++)//

给数组增加10个Int元素 List.Add(i);//..

java什么是同步集合,java类内多个函数如何同步

程序做一些处理

List.RemoveAt(5);//

将第6个元素移除 for( int i=0;i<3;i++)//

再增加3个元素

List.Add(i+20); Int32[] values=

(Int32[])List.ToArray(typeof(Int32));//

返回ArrayList包含的数组。

扩展资料:

Arraylist的定义:

List接口的大小可变数组的实现,位于API文档的java.util.ArrayList<E>。

实现了所有可选列表操作,并允许包括 null在内的所有元素。

除了实现 List接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于 Vector类,除了此类是不同步的。)

size、isEmpty、get、set、iterator和 listIterator操作都以固定时间运行。

add操作以分摊的固定时间运行,也就是说,添加 n个元素需要 O(n)时间。

其他所有操作都以线性时间运行(大体上讲)。

与用于 LinkedList实现的常数因子相比,此实现的常数因子较低。

每个 ArrayList实例都有一个容量。该容量是指用来存储列表元素的数组的大小。

它总是至少等于列表的大小。随着向 ArrayList中不断添加元素,其容量也自动增长。

并未指定增长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那样简单

在添加大量元素前,应用程序可以使用

ensureCapacity操作来增加 ArrayList

实例的容量。这可以减少递增式再分配的数量。

注意,此实现不是同步的。如果多个线程同时访问一个 ArrayList

实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。

(结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。)

这一般通过对自然封装该列表的对象进行同步操作来完成。

如果不存在这样的对象,则应该使用 Collections.synchronizedList方法将该列表“包装”起来。这最好在创建时完成,以防止意外对列表进行不同步的访问:

List list= Collections.synchronizedList(new ArrayList(...));

此类的 iterator和 listIterator方法返回的迭代器是快速失败的。

在创建迭代器之后,除非通过迭代器自身的

remove方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出

ConcurrentModificationException。

因此,面对并发的修改,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。

注意,迭代器的快速失败行为无法得到保证。

因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出

ConcurrentModificationException。

因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测

bug。

参考资料:百度百科------ arraylist

求大神,java的集合框架是什么意思

Java平台提供了一个全新的集合框架。“集合框架”主要由一组用来操作对象的接口组成。不同接口描述一组不同数据类型。

Java 2集合框架图

集合接口:6个接口(短虚线表示),表示不同集合类型,是集合框架的基础。

抽象类:5个抽象类(长虚线表示),对集合接口的部分实现。可扩展为自定义集合类。

实现类:8个实现类(实线表示),对接口的具体实现。

在很大程度上,一旦您理解了接口,您就理解了框架。虽然您总要创建接口特定的实现,但访问实际集合的方法应该限制在接口方法的使用上;因此,允许您更改基本的数据结构而不必改变其它代码。

· Collection接口是一组允许重复的对象。

· Set接口继承 Collection,但不允许重复,使用自己内部的一个排列机制。

· List接口继承 Collection,允许重复,以元素安插的次序来放置元素,不会重新排列。

· Map接口是一组成对的键-值对象,即所持有的是key-value pairs。Map中不能有重复的key。拥有自己的内部排列机制。

·容器中的元素类型都为Object。从容器取得元素时,必须把它转换成原来的类型。

Java 2简化集合框架图

集合接口

1.Collection接口

用于表示任何对象或元素组。想要尽可能以常规方式处理一组元素时,就使用这一接口。

(1)单元素添加、删除操作:

boolean add(Object o):将对象添加给集合

boolean remove(Object o):如果集合中有与o相匹配的对象,则删除对象o

(2)查询操作:

int size():返回当前集合中元素的数量

boolean isEmpty():判断集合中是否有任何元素

boolean contains(Object o):查找集合中是否含有对象o

Iterator iterator():返回一个迭代器,用来访问集合中的各个元素

(3)组操作:作用于元素组或整个集合

boolean containsAll(Collection c):查找集合中是否含有集合c中所有元素

boolean addAll(Collection c):将集合c中所有元素添加给该集合

void clear():删除集合中所有元素

void removeAll(Collection c):从集合中删除集合c中的所有元素

void retainAll(Collection c):从集合中删除集合c中不包含的元素

(4) Collection转换为Object数组:

Object[] toArray():返回一个内含集合所有元素的array

Object[] toArray(Object[] a):返回一个内含集合所有元素的array。运行期返回的array和参数a的型别相同,需要转换为正确型别。

java类内多个函数如何同步

线程间的通讯首要的方式就是对字段及其字段所引用的对象的共享访问。这种通信方式是及其高效的,但是也是导致了可能的错误:线程间相互干涉和内存一致性的问题。避免出现这两种错误的方法就是同步。

线程间相互干扰描述了当多个线程访问共享数据时可能出现的错误。

内存一致性错误描述的了共享内存可能导致的错误。

同步方法(Synchronized method)描述了一种简单的可以有效防止线程间相互干扰及其内存一致性错误的方法。

明锁及同步描述了一种更加通用的同步方法,以及同步是如何基于明锁而实现的。

原子性描述了不能被其它线程干扰的操作。

线程间的相互干扰

考虑下面的一个简单的类Counter:

[java] view plaincopy

class Counter{

private int c= 0;

public void increment(){

c++;

}

public void decrement(){

c--;

}

public int value(){

return c;

}

}

不难看出,其中的increment()方法用来对c加1;decrement()方法用来对c减1。然而,有多个线程中都存在对某个Counter对象的引用,那么线程间的干扰就可能导致出现我们不想要的结果。

线程间的干扰出现在多个线程对同一个数据进行多个操作的时候,也就是出现了“交错”。这就意味着操作是由多个步骤构成的,而此时,在这多个步骤的执行上出现了叠加。

Counter类对象的操作貌似不可能出现这种“交错”,因为其中的两个关于c的操作都很简单,只有一条语句。然而,即使是一条语句也是会被VM翻译成多个步骤的。在这里,我们不深究VM具体上上面的操作翻译成了什么样的步骤。只需要知道即使简单的c++这样的表达式也是会被翻译成三个步骤的:

1.获取c的当前值。

2.对其当前值加1。

3.将增加后的值存储到c中。

表达式c--也是会被按照同样的方式进行翻译,只不过第二步变成了减1,而不是加1。

假定线程A中调用increment方法,线程B中调用decrement方法,而调用时间基本上相同。如果c的初始值为0,那么这两个操作的“交错”顺序可能如下:

1.线程A:获取c的值。

2.线程B:获取c的值。

3.线程A:对获取到的值加1;其结果是1。

4.线程B:对获取到的值减1;其结果是-1。

5.线程A:将结果存储到c中;此时c的值是1。

6.线程B:将结果存储到c中;此时c的值是-1。

这样线程A计算的值就丢失了,也就是被线程B的值覆盖了。上面的这种“交错”只是一种可能性。在不同的系统环境中,有可能是B线程的结果丢失了,或者是根本就不会出现错误。由于这种“交错”是不可预测的,线程间相互干扰造成的缺陷是很难定位和修改的。

内存一致性错误

内存一致性错误发生在不同线程对同一数据产生不同的“见解”。导致内存一致性错误的原因很负责,超出了本文的描述范围。庆幸的是,程序员并不需要知道出现这些原因的细节。我们需要的只是一种可以避免这种错误的方法。

避免出现内存一致性错误的关键在于理解“先后顺序”关系。这种关系是一种简单的方法,能够确保一条语句对内存的写操作对于其它特定的语句都是可见的。为了理解这点,我们可以考虑如下的示例。假定定义了一个简单的int类型的字段并对其进行了初始化:

int counter= 0;

该字段由两个线程共享:A和B。假定线程A对counter进行了自增操作:

counter++;

然后,线程B输出counter的值:

System.out.println(counter);

如果以上两条语句是在同一个线程中执行的,那么输出的结果自然是1。但是如果这两条语句是在两个不同的线程中,那么输出的结构有可能是0。这是因为没有保证线程A对counter的修改对线程B来说是可见的。除非程序员在这两条语句间建立了一定的“先后顺序”。

我们可以采取多种方式建立这种“先后顺序”。使用同步就是其中之一,这点我们将会在下面的小节中看到。

到目前为止,我们已经看到了两种建立这种“先后顺序”的方式:

当一条语句中调用了Thread.start()方法,那么每一条和该语句已经建立了“先后顺序”的语句都和新线程中的每一条语句有着这种“先后顺序”。引入并创建这个新线程的代码产生的结果对该新线程来说都是可见的。

当一个线程终止了并导致另外的线程中调用join的语句回,那么此时这个终止了的线程中执行了的所有语句都和随后的join语句随后的所有语句建立了这种“先后关系”。也就是说终止了的线程中的代码效果对调用join方法的线程来说是可见。

关于哪些操作可以建立这种“先后关系”,更多的信息请参阅“java.util.concurrent包的概要说明”。

同步方法

Java编程语言中提供了两种基本的同步用语:同步方法和同步语句。同步语句相对而言更为复杂一些,我们将在下一小节中进行描述。本节重点讨论同步方法。

我们只需要在声明方法的时候增加关键字synchronized即可:

[java] view plaincopy

public class SynchronizedCounter{

private int c= 0;

public synchronized void increment(){

c++;

}

public synchronized void decrement(){

c--;

}

public synchronized int value(){

return c;

}

}

如果count是SynchronizedCounter类的实例,设置其方法为同步方法将有一下两个效果:

首先,不可能出现对同一对象的同步方法的两个调用的“交错”。当一个线程在执行一个对象的同步方式的时候,其他所有的调用该对象的同步方法的线程对会被挂起,直到第一个线程对该对象操作完毕。

其次,当一个同步方法退出时,会自动与该对象的后续同步方法间建立“先后顺序”的关系。这就确保了对该对象的修改对其他线程是可见的。

注意:构造函数不能为同步的——在构造函数前使用synchronized关键字将导致语义错误。同步构造函数是没有意义的。这是因为只有创建该对象的线程才能调用其构造函数。

警告:在创建多个线程共享的对象时,要特别小心对该对象的引用不能过早地“泄露”。例如,假定我们想要维护一个保存类的所有实例的列表instances。我们可能会在构造函数中这样写到:

instances.add(this);

但是,其他线程可会在该对象的构造完成之前就访问该对象。

同步方法是一种简单的可以避免线程相互干扰和内存一致性错误的策略:如果一个对象对多个线程都是可见的,那么所有对该对象的变量的读写都应该是通过同步方法完成的(一个例外就是final字段,他在对象创建完成后是不能被修改的,因此,在对象创建完毕后,可以通过非同步的方法对其进行安全的读取)。这种策略是有效的,但是可能导致“liveness”问题。这点我们会在本课程的后面进行描述。

内部锁及同步

同步是构建在被称为“内部锁或者是监视锁”的内部实体上的。(在API中通常被称为是“监视器”。)内部锁在两个方面都扮演着重要的角色:保证对对象访问的排他性和建立也对象可见性相关的重要的“先后顺序”。

每一个对象都有一个与之相关联动的内部锁。按照传统的做法,当一个线程需要对一个对象的字段进行排他性访问并保持访问的一致性时,他必须在访问前先获取该对象的内部锁,然后才能访问之,最后释放该内部锁。在线程获取对象的内部锁到释放对象的内部锁的这段时间,我们说该线程拥有该对象的内部锁。只要有一个线程已经拥有了一个内部锁,其他线程就不能在拥有该锁了。其他线程将会在试图获取该锁的时候被阻塞了。

当一个线程释放了一个内部锁,那么就会建立起该动作和后续获取该锁之间的“先后顺序”。

同步方法中的锁

当一个线程调用一个同步方法的时候,他就自动地获得了该方法所属对象的内部锁,并在方法返回的时候释放该锁。即使是由于出现了没有被捕获的异常而导致方法返回,该锁也会被释放。

我们可能会感到疑惑:当调用一个静态的同步方法的时候会怎样了,静态方法是和类相关的,而不是和对象相关的?在这种情况下,线程获取的是该类的类对象的内部锁。这样对于静态字段的方法是通过一个和类的实例的锁相区分的另外的锁来进行的。

同步语句

另外一种创建同步代码的方式就是使用同步语句。和同步方法不同,使用同步语句是必须指明是要使用哪个对象的内部锁:

[java] view plaincopy

public void addName(String name){

synchronized(this){

lastName= name;

nameCount++;

}

nameList.add(name);

}

在上面的示例中,方法addName需要对lastName和nameCount的修改进行同步,还要避免同步调用其他对象的方法(在同步代码段中调用其他对象的方法可能导致“Liveness”中描述的问题)。如果没有使用同步语句,那么将不得不使用一个单独的,未同步的方法来完成对nameList.add的调用。

在改善并发性时,巧妙地使用同步语句能起到很大的帮助作用。例如,我们假定类MsLunch有两个实例字段,c1和c2,这两个变量绝不会一起使用。所有对这两个变量的更新都需要进行同步。但是没有理由阻止对c1的更新和对c2的更新出现交错——这样做会创建不必要的阻塞,进而降低并发性。此时,我们没有使用同步方法或者使用和this相关的锁,而是创建了两个单独的对象来提供锁。

[java] view plaincopy

public class MsLunch{

private long c1= 0;

private long c2= 0;

private Object lock1= new Object();

private Object lock2= new Object();

public void inc1(){

synchronized(lock1){

c1++;

}

}

public void inc2(){

synchronized(lock2){

c2++;

}

}

}

采用这种方式时需要特别的小心。我们必须绝对确保相关字段的访问交错是完全安全的。

同步的重入

回忆前面提到的:线程不能获取已经被别的线程获取的锁。单丝线程可以获取自身已经拥有的锁。允许一个线程能重复获得同一个锁就称为同步重入。它是这样的一种情况:在同步代码中直接或者间接地调用了还有同步代码的方法,两个同步代码段中使用的是同一个锁。如果没有同步重入,在编写同步代码时需要额外的小心,以避免线程将自己阻塞。

原子性

在编程中,原子性动作就是指一次性有效完成的动作。原子性动作是不能在中间停止的:要么一次性完全执行完毕,要么就不执行。在动作没有执行完毕之前,是不会产生可见结果的。

通过前面的示例,我们已经发现了诸如c++这样的自增表达式并不属于原子操作。即使是非常见到的表达式也定义了负责的动作,这些动作可以被解释成许多别的动作。然而,的确存在一些原子操作的:

对几乎所有的原生数据类型变量的读写(除了long和都变了外)以及引用变量的读写都是原子的。

对所有声明为volatile的变量的读写都是原子的,包括long和double类型。

原子性动作是不会出现交错的,因此,使用这些原子性动作时不用考虑线程间的干扰。然而,这并不意味着可以移除对原子操作的同步。因为内存一致性错误还是有可能出现的。使用volatile变量可以减少内存一致性错误的风险,因为任何对volatile变量的写操作都和后续对该变量的读操作建立了“先后顺序”。这就意味着对volatile类型变量的修改对于别的线程来说是可见的。更重要的是,这意味着当一个线程读取一个volatile类型的变量是,他看到的不仅仅是对该变量的最后一次修改,还看到了导致这种修改的代码带来的其他影响。

使用简单的原子变量访问比通过同步代码来访问变量更高效,但是需要程序员的更多细心考虑,以避免内存一致性错误。这种额外的付出是否值得完全取决于应用程序的大小和复杂度。

在包java.util.concurrent中的一些类提供了原子方法,这些方法不依赖于同步。我们会在章节:High Level Concurrency Objects中进行讨论。

OK,本文到此结束,希望对大家有所帮助。

学java到底是干什么(Java是什么Java到底能干嘛)假面骑士drivesurprisefuture