`
youyu4
  • 浏览: 440126 次
社区版块
存档分类
最新评论

Java多线程之线程安全

 
阅读更多

Java多线程之线程安全

 

保证线程安全的方法

 

  1. Synchronized,同步方法,同步块
  2. Volatile,修饰变量
  3. Lock,可重入锁,读写锁
  4. 不变类,final class,很少使用
  5. 线程安全类
  6. 不使用共享变量
  7. ThreadLocal

 

 

线程安全类

 

  1. Vector、Hashtable,如果不涉及线程安全,用这些类就会浪费资源
  2. 用静态同步方法封装非同步集合
// Collections中有很多静态的同步方法,来同步那些非同步的集合
List list = Collections.synchronizedList(new ArrayList());

 

 

 

ThreadLocal

 

给每个线程一个变量拷贝,由线程自己来维护,导致每个线程间的这个变量相互独立,也就没有了共享变量,所以线程安全。

 

在高并发场景:如果不考虑延迟、共享数据,这会是个不错的选择。

 

 

 

不可变对象

 

不可变对象可以在没有同步的情况下共享,降低访问时的同步 开销。

 

创建不可变类

  • 全部变量都是私有的
  • 通过构造函数初始化私有变量
  • 没有setter方法,只有getter方法
  • getter方法不要直接返回对象本身,要返回克隆对象

 

 

 

线程安全单例

 

对于单例模式,我们需要注意的是,有一些单例的写法是线程安全的,而有些是非线程安全的。

 

饿汉单例(线程安全)

 

// 在类装载时就实例化
public class Singleton {
    private static Singleton sin=new Singleton();    ///直接初始化一个实例对象
    private Singleton(){    ///private类型的构造函数,保证其他类对象不能直接new一个该对象的实例
    }
    public static Singleton getSin(){    ///该类唯一的一个public方法    
        return sin;
    }
}
 

 

懒汉单例(非线程安全)

 

// 在使用时再实例化
public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
  
    public static Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}
 

 

加锁的懒汉单例(线程安全)

 

// 这种加锁的方式虽然能解决线程安全,但是影响性能
public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}
 

 

双重校验锁的懒汉单例(线程安全)

 

// 保证了线程安全和性能
public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}
 

 

 

 

常用的同步类

 

  • CountDownLatch闭锁,运动员计时
  • Semaphone信号量,并发资源访问控制
  • CyclicBarrier栅栏,游戏中等待所有人到了,再进入下一关
  • Phaser:一种可重用的同步屏障,功能上类似于CyclicBarrier和CountDownLatch,但使用上更为灵活。
  • Exchanger:允许两个线程在某个汇合点交换对象,在某些管道设计时比较有用。

 

CountDownLatch

 

一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

 

主要方法

// 构造方法参数指定了计数的次数
public CountDownLatch(int count);

// 当前线程调用此方法,则计数减一
public void countDown();

// 调用此方法会一直阻塞当前线程,直到计时器的值为0
public void await() throws InterruptedException

 

代码

public class CountDownLatchDemo {  
    final static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
    public static void main(String[] args) throws InterruptedException {  
        CountDownLatch latch=new CountDownLatch(2);//两个工人的协作  
        Worker worker1=new Worker("zhang san", 5000, latch);  
        Worker worker2=new Worker("li si", 8000, latch);  
        worker1.start();//  
        worker2.start();//  
        latch.await();//等待所有工人完成工作  
        System.out.println("all work done at "+sdf.format(new Date()));  
    }  
      
      
    static class Worker extends Thread{  
        String workerName;   
        int workTime;  
        CountDownLatch latch;  
        public Worker(String workerName ,int workTime ,CountDownLatch latch){  
             this.workerName=workerName;  
             this.workTime=workTime;  
             this.latch=latch;  
        }  
        public void run(){  
            System.out.println("Worker "+workerName+" do work begin at "+sdf.format(new Date()));  
            doWork();//工作了  
            System.out.println("Worker "+workerName+" do work complete at "+sdf.format(new Date()));  
            latch.countDown();//工人完成工作,计数器减一  
  
        }  
          
        private void doWork(){  
            try {  
                Thread.sleep(workTime);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
      
       
} 

 

结果

Worker zhang san do work begin at 2011-04-14 11:05:11

Worker li si do work begin at 2011-04-14 11:05:11

Worker zhang san do work complete at 2011-04-14 11:05:16

Worker li si do work complete at 2011-04-14 11:05:19

all work done at 2011-04-14 11:05:19

 

 

Semaphore

 

厕所有5个坑,假如有10个人要上厕所,那么同时只能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中 的任何一个人让开后,其中等待的另外5个人中又有一个人可以占用了。另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。

 

主要方法

// 构造函数,定义只有5个信号量
Semaphore(5)

// 获取一个许可,如果没有就等待
acquire()

// 释放一个许可
release()

 

代码

package com.test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class TestSemaphore {

    public static void main(String[] args) {

        // 线程池
        ExecutorService exec = Executors.newCachedThreadPool();

        // 只能5个线程同时访问
        final Semaphore semp = new Semaphore(5);
        
        // 模拟20个客户端访问
        for (int index = 0; index < 20; index++) {

            final int NO = index;
            Runnable run = new Runnable() {
                public void run() {

                    try {
                            // 获取许可
                            semp.acquire();
                            System.out.println("Accessing: " + NO);
                            Thread.sleep((long) (Math.random() * 10000));
                            // 访问完后,释放
                            semp.release();
                            System.out.println("-----------------"+semp.availablePermits());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
    
                    }
                }

            };
            exec.execute(run);
        }
        // 退出线程池
        exec.shutdown();
       }
}

 

结果

Accessing: 0

Accessing: 1

Accessing: 3

Accessing: 4

Accessing: 2

-----------------0

Accessing: 6

-----------------1

Accessing: 7

-----------------1

Accessing: 8

-----------------1

Accessing: 10

-----------------1

Accessing: 9

-----------------1

Accessing: 5

-----------------1

Accessing: 12

-----------------1

Accessing: 11

-----------------1

Accessing: 13

-----------------1

Accessing: 14

-----------------1

Accessing: 15

-----------------1

Accessing: 16

-----------------1

Accessing: 17

-----------------1

Accessing: 18

-----------------1

Accessing: 19

 

 

 

CyclicBarrier

 

循环障栅栏,一组线程写操作,并且只有所有线程完成写操作,才继续做后面的事

 

常用方法

// 构造函数,定义三个线程
CyclicBarrier(3);

// 当所有参与者都调用await()之前,前面执行完的线程一直等待
await()

 

代码

public class CyclicBarrierTest {  
  
    public static void main(String[] args) throws IOException, InterruptedException {  
        //如果将参数改为4,但是下面只加入了3个选手,这永远等待下去  
        //Waits until all parties have invoked await on this barrier.   
        CyclicBarrier barrier = new CyclicBarrier(3);  
  
        ExecutorService executor = Executors.newFixedThreadPool(3);  
        executor.submit(new Thread(new Runner(barrier, "1号选手")));  
        executor.submit(new Thread(new Runner(barrier, "2号选手")));  
        executor.submit(new Thread(new Runner(barrier, "3号选手")));  
  
        executor.shutdown();  
    }  
}  
  
class Runner implements Runnable {  
    // 一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)  
    private CyclicBarrier barrier;  
  
    private String name;  
  
    public Runner(CyclicBarrier barrier, String name) {  
        super();  
        this.barrier = barrier;  
        this.name = name;  
    }  
  
    @Override  
    public void run() {  
        try {  
            Thread.sleep(1000 * (new Random()).nextInt(8));  
            System.out.println(name + " 准备好了...");  
            // barrier的await方法,在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。  
            barrier.await();  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        } catch (BrokenBarrierException e) {  
            e.printStackTrace();  
        }  
        System.out.println(name + " 起跑!");  
    }  
}

 

结果

3号选手 准备好了...

2号选手 准备好了...

1号选手 准备好了...

1号选手 起跑!

2号选手 起跑!

3号选手 起跑!

 

 

 

为什么只有ConcurrentHashMap,没有Concurrent的List?

 

       从上面的描述,可以体现出Concurrent 比 CopyOnWrite要好,因为保证线程安全和并发瓶颈,那为什么List没有Concurrent实习那呢?

 

原因

 

       Concurrent的List很难实现,例如:contains() 这样一个操作来说,当你进行搜索的时候如何避免锁住整个list?

 

       同时,这也与ConcurrentHashMap的实现方式有关,一个ConcurrentHashMap由多个segment组成,每一个segment都包含了一个HashEntry数组的hashtable, 每一个segment包含了对自己的hashtable的操作,比如get,put,replace等操作,这些操作发生的时候,对自己的hashtable进行锁定。由于每一个segment写操作只锁定自己的hashtable,所以可能存在多个线程同时写的情况,性能无疑好于只有一个hashtable锁定的情况。

 

     同理,由于HashTable的value不能为null,所以就没有ConcurrentHashSet。

 

分享到:
评论

相关推荐

    Java多线程设计模式上传文件

    Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式...

    java 多线程操作数据库

    ### Java多线程操作数据库:深入解析与应用 在当今高度并发的应用环境中,Java多线程技术被广泛应用于处理数据库操作,以提升系统的响应速度和处理能力。本文将基于一个具体的Java多线程操作数据库的应用程序,深入...

    Java多线程知识点总结

    Java多线程是Java编程语言中一个非常重要的概念,它允许开发者在一个程序中创建多个执行线程并行运行,以提高程序的执行效率和响应速度。在Java中,线程的生命周期包含五个基本状态,分别是新建状态(New)、就绪...

    java多线程的讲解和实战

    Java多线程是Java编程中的重要概念,尤其在如今的多核处理器环境下,理解并熟练掌握多线程技术对于提高程序性能和响应速度至关重要。本资料详细讲解了Java多线程的原理,并提供了丰富的实战代码,非常适合Java初学者...

    java多线程ppt

    java多线程PPT 多线程基本概念 创建线程的方式 线程的挂起与唤醒 多线程问题

    java多线程读取文件

    Java多线程读大文件 java多线程写文件:多线程往队列中写入数据

    Java多线程与线程安全实践(基于Http协议的断点续传)源码.zip

    Java多线程与线程安全实践Java多线程与线程安全实践Java多线程与线程安全实践Java多线程与线程安全实践Java多线程与线程安全实践Java多线程与线程安全实践Java多线程与线程安全实践Java多线程与线程安全实践Java多...

    java多线程经典案例

    Java多线程是Java编程中的重要概念,它允许程序同时执行多个任务,极大地提升了程序的效率和性能。在Java中,实现多线程有两种主要方式:通过实现Runnable接口或者继承Thread类。本案例将深入探讨Java多线程中的关键...

    java多线程Demo

    Java多线程是Java编程中的一个重要概念,它允许程序同时执行多个任务,提高了程序的效率和响应速度。在Java中,实现多线程有两种主要方式:继承Thread类和实现Runnable接口。 1. 继承Thread类: 当我们创建一个新...

    java多线程处理数据库数据

    1. **线程安全**:由于多线程环境下可能存在数据竞争,所以在访问共享资源(如数据库连接)时,需要确保线程安全。可以使用`synchronized`关键字或者`Lock`来同步访问。 2. **事务管理**:在多线程环境中,可能需要...

    JAVA单线程多线程

    ### JAVA中的单线程与多线程概念解析 #### 单线程的理解 在Java编程环境中,单线程指的是程序执行过程中只有一个线程在运行。这意味着任何时刻只能执行一个任务,上一个任务完成后才会进行下一个任务。单线程模型...

    java多线程分页查询

    ### Java多线程分页查询知识点详解 #### 一、背景与需求分析 在实际的软件开发过程中,尤其是在处理大量数据时,如何高效地进行数据查询成为了一个关键问题。例如,在一个用户众多的社交平台上,当用户需要查看...

    java 多线程并发实例

    在Java编程中,多线程并发是提升程序执行效率、充分利用多核处理器资源的重要手段。本文将基于"java 多线程并发实例"这个主题,深入探讨Java中的多线程并发概念及其应用。 首先,我们要了解Java中的线程。线程是...

    java多线程查询数据库

    综上所述,"java多线程查询数据库"是一个涉及多线程技术、线程池管理、并发控制、分页查询等多个方面的复杂问题。通过理解和掌握这些知识点,我们可以有效地提高数据库操作的效率和系统的响应速度。

    汪文君JAVA多线程编程实战(完整不加密)

    《汪文君JAVA多线程编程实战》是一本专注于Java多线程编程的实战教程,由知名讲师汪文君倾力打造。这本书旨在帮助Java开发者深入理解和熟练掌握多线程编程技术,提升软件开发的效率和质量。在Java平台中,多线程是...

    JAVAJAVA多线程教学演示系统论文

    《JAVA多线程教学演示系统》是一篇深入探讨JAVA多线程编程的论文,它针对教育领域中的教学需求,提供了一种生动、直观的演示方式,帮助学生更好地理解和掌握多线程技术。这篇论文的核心内容可能包括以下几个方面: ...

    JAVA多线程练习题答案。

    JAVA多线程练习题答案详解 在本文中,我们将对 JAVA 多线程练习题的答案进行详细的解释和分析。这些题目涵盖了 JAVA 多线程编程的基本概念和技术,包括线程的生命周期、线程同步、线程状态、线程优先级、线程安全等...

    JAVA多线程编程技术PDF

    总结起来,“JAVA多线程编程技术PDF”涵盖了多线程的基本概念、同步机制、线程通信、死锁避免、线程池以及线程安全的集合类等内容。通过深入学习这份资料,开发者可以全面掌握Java多线程编程技术,提升程序的并发...

    Java多线程机制(讲述java里面与多线程有关的函数)

    Java多线程机制是Java编程中至关重要的一部分,它允许程序同时执行多个任务,提升应用程序的效率和响应性。以下是对各个知识点的详细说明: 9.1 Java中的线程: Java程序中的线程是在操作系统级别的线程基础上进行...

    Java多线程编程核心技术_完整版_java_

    Java多线程编程是Java开发中的重要组成部分,它允许程序同时执行多个任务,极大地提高了程序的效率和响应性。在Java中,多线程主要通过继承Thread类或实现Runnable接口来实现。本教程《Java多线程编程核心技术》将...

Global site tag (gtag.js) - Google Analytics