资讯

精准传达 • 有效沟通

从品牌网站建设到网络营销策划,从策略到执行的一站式服务

java线程安全的代码 java 实现线程安全

Java Swing开发中的线程安全

SwingAPI的设计目标是强大 灵活和易用 非凡地 我们希望能让程序员们方便地建立新的Swing组件 不论是从头开始还是通过扩展我们所提供的一些组件 出于这个目的 我们不要求Swing组件支持多线程访问 相反 我们向组件发送请求并在单一线程中执行请求 本文讨论线程和Swing组件 目的不仅是为了帮助你以线程安全的方式使用SwingAPI 而且解释了我们为什么会选择现在这样的线程方案 本文包括以下内容

企业建站必须是能够以充分展现企业形象为主要目的,是企业文化与产品对外扩展宣传的重要窗口,一个合格的网站不仅仅能为公司带来巨大的互联网上的收集和信息发布平台,成都创新互联公司面向各种领域:塔吊租赁网站设计全网营销推广解决方案、网站设计等建站排名服务。


单线程规则 Swing线程在同一时刻仅能被一个线程所访问 一般来说 这个线程是事件派发线程 规则的例外 有些操作保证是线程安全的 事件分发 假如你需要从事件处理或绘制代码以外的地方访问UI 那么你可以使用SwingUtilities类的invokeLater要求在事件派发线程中执行某些代码 这个方法会立即返回 不会等待代码执行完毕 invokeAndWait行为与invokeLater类似 除了这个方法会等待代码执行完毕 一般地 你可以用invokeLater来代替这个方法 下面是一些使用这几个API的例子 请同时参阅《TheJavaTutorial》中的 BINGOexample 尤其是以下几个类 CardWindow ControlPane Player和OverallStatusPane

使用invokeLater方法你可以从任何线程调用invokeLater方法以请求事件派发线程运行特定代码 你必须把要运行的代码放到一个Runnable对象的run方法中 并将此Runnable对象设为invokeLater的参数 invokeLater方法会立即返回 不等待事件派发线程执行指定代码 这是一个使用invokeLater方法的例子

 RunnabledoWorkRunnable=newRunnable };

SwingUtilities invokeLater;使用invokeAndWait方法invokeAndWait方法和invokeLater方法很相似 除了invokeAndWait方法会等事件派发线程执行了指定代码才返回 在可能的情况下 你应该尽量用invokeLater来代替invokeAndWait 假如你真的要使用invokeAndWait 请确保调用invokeAndWait的线程不会在调用期间持有任何其他线程可能需要的锁

这是一个使用invokeAndWait的例子

 voidshowHelloThereDialogthrowsException }; SwingUtilities invokeAndWait; }

类似地 假设一个线程需要对GUI的状态进行存取 比如文本域的内容 它的代码可能类似这样

 voidprintTextField throwsException }; SwingUtilities invokeAndWait; System out println;}

假如你能避免使用线程 最好这样做 线程可能难于使用 并使得程序的debug更困难 一般来说 对于严格意义下的GUI工作 线程是不必要的 比如对组件属性的更新 不管怎么说 有时候线程是必要的 下列情况是使用线程的一些典型情况 执行一项费时的任务而不必将事件派发线程锁定 例子包括执行大量计算的情况 会导致大量类被装载的情况 和为网络或磁盘I/O而阻塞的情况 重复地执行一项操作 通常在两次操作间间隔一个预定的时间周期 要等待来自客户的消息 你可以使用两个类来帮助你实现线程 SwingWorker 创建一个后台线程来执行费时的操作 Timer 创建一个线程来执行或多次执行某些代码 在两次执行间间隔用户定义的延迟 使用SwingWorker类SwingWorker类在SwingWorker java中实现 这个类并不包含在Java的任何发行版中 所以你必须单独下载它 SwingWorker类做了所有实现一个后台线程所需的肮脏工作 虽然许多程序都不需要后台线程 后台线程在执行费时的操作时仍然是很有用的 它能提高程序的性能观感

SwingWorkersanexampleofusingSwingWorker 要使用SwingWorker类 你首先要实现它的一个子类 在子类中 你必须实现construct方法还包含你的长时间操作 当你实例化SwingWorker的子类时 SwingWorker创建一个线程但并不启动它 你要调用你的SwingWorker对象的start方法来启动线程 然后start方法会调用你的construct方法 当你需要construct方法返回的对象时 可以调用SwingWorker类的get方法 这是一个使用SwingWorker类的例子

  //在main方法中 finalSwingWorkerworker=newSwingWorker }; worker start; //在动作事件处理方法中 JOptionPane showMessageDialog)

当程序的main方法调用start方法 SwingWorker启动一个新的线程来实例化ExpensiveDialogComponent main方法还构造了由一个窗口和一个按钮组成的GUI 当用户点击按钮 程序将阻塞 假如必要 阻塞到ExpensiveDialogComponent创建完成 然后程序显示一个包含ExpensiveDialogComponent的模式对话框 你可以在MyApplication java找到整个程序 使用Timer类Timer类通过一个ActionListener来执行或多次执行一项操作 你创建定时器的时候可以指定操作执行的频率 并且你可以指定定时器的动作事件的监听者 启动定时器后 动作监听者的actionPerformed方法会被调用来执行操作 定时器动作监听者定义的actionPerformed方法将在事件派发线程中调用 这意味着你不必在其中使用invokeLater方法 这是一个使用Timer类来实现动画循环的例子

 publicclassAnimatorApplicationTimer extendsJFrameimplementsActionListener publicvoidstartAnimationelse } publicvoidstopAnimation publicvoidactionPerformed }

在一个线程中执行所有的用户界面代码有这样一些优点 组件开发者不必对线程编程有深入的理解 像ViewPoint和Trestle这类工具包中的所有组件都必须完全支持多线程访问 使得扩展非常困难 尤其对不精通线程编程的开发者来说 最近的一些工具包如SubArctic和IFC 都采用和Swing类似的设计 事件以可预知的次序派发 invokeLater排队的runnable对象从鼠标和键盘事件 定时器事件 绘制请求的同一个队列派发 在一些组件完全支持多线程访问的工具包中 组件的改变被变化无常的线程调度程序穿插到事件处理过程中 这使得全面测试变得困难甚至不可能 更低的代价 尝试小心锁住临界区的工具包要花费实足的时间和空间在锁的治理上 每当工具包中调用某个可能在客户代码中实现的方法时 工具包都要保存它的状态并释放所有锁 以便客户代码能在必要时获得锁 当控制权交回到工具包 工具包又必须重新抓住它的锁并恢复状态 所有应用程序都不得不负担这一代价 即使大多数应用程序并不需要对GUI的并发访问 这是的SubArcticJavaToolkit的对在工具包中支持多线程访问的问题的描述 我们的基本信条是 当设计和建造多线程应用程序 尤其是那些包括GUI组件的应用程序时 必须保证极端小心 线程的使用可能会很有欺骗性 在许多情况下 它们表现得能够极好的简化编成 使得设计 专注于单一任务的简单自治实体 成为可能 在一些情况下它们的确简化了设计和编码 然而 在几乎所有的情况下 它们都使得调试 测试和维护的困难大大增加甚至成为不可能 无论大多数程序员所受的练习 他们的经验和实践 还是我们用来帮助自己的工具 都不是能够用来对付非决定论的 例如 全面测试在bug依靠于时间时是几乎不可能的 尤其对于Java来说 一个程序要运行在许多不同类型的机器的操作系统平台上 并且每个程序都必须在抢先和非抢先式调度下都能正常工作 由于这些固有的困难 我们力劝你三思是否绝对有使用线程的必要 尽管如此 有些情况下使用线程是必要的 所以subArctic提供了一个线程安全的访问机制

lishixinzhi/Article/program/Java/gj/201311/27616

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

Java的List如何实现线程安全?

Java的List如何实现线程安全?

Collections.synchronizedList(names);效率最高,线程安全

Java的List是我们平时很常用的集合,线程安全对于高并发的场景也十分的重要,那么List如何才能实现线程安全呢 ?

加锁

首先大家会想到用Vector,这里我们就不讨论了,首先讨论的是加锁,例如下面的代码

public class Synchronized{

private ListString  names = new LinkedList();

public synchronized void addName(String name ){

names.add("abc");

}

public String getName(Integer index){

Lock lock =new ReentrantLock();

lock.lock();

try {

return names.get(index);

}catch (Exception e){

e.printStackTrace();

}

finally {

lock.unlock();

}

return null;

}

}

synchronized一加,或者使用lock 可以实现线程安全,但是这样的List要是很多个,代码量会大大增加。

java自带类

在java中我找到自带有两种方法

CopyOnWriteArrayList

CopyOnWrite 写入时复制,它使一个List同步的替代品,通常情况下提供了更好的并发性,并且避免了再迭代时候对容器的加锁和复制。通常更适合用于迭代,在多插入的情况下由于多次的复制性能会一定的下降。

下面是add方法的源代码

public boolean add(E e) {

final ReentrantLock lock = this.lock; // 加锁 只允许获得锁的线程访问

lock.lock();

try {

Object[] elements = getArray();

int len = elements.length;

// 创建个长度加1的数组并复制过去

Object[] newElements = Arrays.copyOf(elements, len + 1);

newElements[len] = e; // 赋值

setArray(newElements); // 设置内部的数组

return true;

} finally {

lock.unlock();

}

}

Collections.synchronizedList

Collections中有许多这个系列的方法例如

主要是利用了装饰者模式对传入的集合进行调用 Collotions中有内部类SynchronizedList

static class SynchronizedListE

extends SynchronizedCollectionE

implements ListE {

private static final long serialVersionUID = -7754090372962971524L;

final ListE list;

SynchronizedList(ListE list) {

super(list);

this.list = list;

}

public E get(int index) {

synchronized (mutex) {return list.get(index);}

}

public E set(int index, E element) {

synchronized (mutex) {return list.set(index, element);}

}

public void add(int index, E element) {

synchronized (mutex) {list.add(index, element);}

}

public E remove(int index) {

synchronized (mutex) {return list.remove(index);}

}

static class SynchronizedCollectionE implements CollectionE, Serializable {

private static final long serialVersionUID = 3053995032091335093L;

final CollectionE c;  // Backing Collection

final Object mutex;     // Object on which to synchronize

这里上面的mutex就是锁的对象 在构建时候可以指定锁的对象 主要使用synchronize关键字实现线程安全

/**

* @serial include

*/

static class SynchronizedListE

extends SynchronizedCollectionE

implements ListE {

private static final long serialVersionUID = -7754090372962971524L;

final ListE list;

SynchronizedList(ListE list) {

super(list);

this.list = list;

}

SynchronizedList(ListE list, Object mutex) {

super(list, mutex);

this.list = list;

}

这里只是列举SynchronizedList ,其他类类似,可以看下源码了解下。

测试

public class Main {

public static void main(String[] args) {

ListString names = new LinkedList();

names.add("sub");

names.add("jobs");

// 同步方法1 内部使用lock

long a = System.currentTimeMillis();

ListString strings = new CopyOnWriteArrayList(names);

for (int i = 0; i 100000; i++) {

strings.add("param1");

}

long b = System.currentTimeMillis();

// 同步方法2 装饰器模式使用 synchronized

ListString synchronizedList = Collections.synchronizedList(names);

for (int i = 0; i 100000; i++) {

synchronizedList.add("param2");

}

long c = System.currentTimeMillis();

System.out.println("CopyOnWriteArrayList time == "+(b-a));

System.out.println("Collections.synchronizedList time == "+(c-b));

}

}

两者内部使用的方法都不一样,CopyOnWriteArrayList内部是使用lock进行加锁解锁完成单线程访问,synchronizedList使用的是synchronize

进行了100000次添加后时间对比如下:

可以看出来还是使用了synchronize的集合工具类在添加方面更加快一些,其他方法这里篇幅关系就不测试了,大家有兴趣去试一下。


分享文章:java线程安全的代码 java 实现线程安全
网页URL:http://cdkjz.cn/article/ddjsshh.html
多年建站经验

多一份参考,总有益处

联系快上网,免费获得专属《策划方案》及报价

咨询相关问题或预约面谈,可以通过以下方式与我们联系

大客户专线   成都:13518219792   座机:028-86922220