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

Java多线程

    博客分类:
  • java
阅读更多

下面的代码如果没有出现线程冲突结果应该是200,但实际的结果在100到200之间:

  1. class Foo{
  2. public String x = "";
  3. public void add(){
  4. x+="1";
  5. }
  6. }
  7. public class Main extends Thread{
  8. public boolean flag;
  9. public Foo foo;
  10. public Main(boolean flag){
  11. this.flag = flag;
  12. }
  13. public void run(){
  14. for(int i = 0; i < 100; i++){
  15. foo.add();
  16. }
  17. }
  18. public static void main(String[] arg){
  19. Foo foo = new Foo();
  20. Main a = new Main(true);
  21. Main b = new Main(false);
  22. a.foo = b.foo = foo;
  23. a.start();
  24. b.start();
  25. try {
  26. a.join();
  27. b.join();
  28. } catch (Exception e) {}
  29. System.out.println(foo.x.length());
  30. }
  31. }

在Foo中加入同步的代码之后,不管重复多少次结果都是200:

1 class Foo{
2     public String x = "";
3     public void add(){
4         synchronized(this){
5             x+="1";
6         }        
7     }
8 }

可以使用Object的wait和notify来等待对象/通知等待的线程:

  1. class Foo extends Thread{
  2. public int total;
  3. public void run(){
  4. synchronized (this) {
  5. for(int i = total = 0; i < 100; i++){
  6. total = total + 1;
  7. }
  8. this.notify();
  9. }
  10. }
  11. }
  12. public class Main extends Thread{
  13. public static void main(String[] arg){
  14. Foo foo = new Foo();
  15. foo.start();
  16. synchronized(foo){
  17. try {
  18. foo.wait();
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. System.out.println("计算结果为:"+foo.total);
  23. }
  24. }
  25. }

从上面的代码中看到每个对象都可以当做一个锁来用,Object中也提供了同步的方法:wait、notify、notifyAll。

发现如果notifyAll不再synchronized中,就会出现异常:通知调用的是本地方法,可能有用公共的东西?

如果没有调用notifyAll,那么线程就会一直等待下去而不会结束。

 1 class Calculator extends Thread {
 2     int total;
 3     public void run() {
 4         synchronized (this) {
 5             System.out.println("Calculator 正在运行");
 6             for (int i = 0; i < 10000; i++) {
 7                 try {
 8                     Thread.sleep(1);
 9                 } catch (InterruptedException e) {
10                     e.printStackTrace();
11                 }
12                 total += i;
13             }
14             notifyAll();
15         }
16     }
17 }
18 public class Main extends Thread {
19     Calculator c;
20     public Main(Calculator c) {
21         this.c = c;
22     }
23     public void run() {
24         synchronized (c) {
25             try {
26                 System.out.println(Thread.currentThread() + "等待计算结果。。。");
27                 c.wait();
28             } catch (InterruptedException e) {
29             }
30             System.out.println(Thread.currentThread() + "计算结果为:" + c.total);
31         }
32     }
33     public static void main(String[] args) {
34         Calculator calculator = new Calculator();        
35         new Main(calculator).start();
36         new Main(calculator).start();
37         new Main(calculator).start();  
38         calculator.start();
39     }
40 }

java中的join和C编程中的join差不多,可以发现不管怎么执行,只要执行过join方法之后,主线程都会等线程Worker执行完成后再继续执行:

  1. class Worker extends Thread{
  2. public void run(){
  3. for(int i = 0; i < 10; i++){
  4. System.out.println("线程第"+i+"次执行");
  5. }
  6. }
  7. }
  8. public class Main{
  9. public static void main(String[] arg){
  10. Worker a = new Worker();
  11. a.start();
  12. for(int i = 0; i < 20; i++){
  13. System.out.println("主线程第"+i+"次执行");
  14. if(i > 2){
  15. try{
  16. a.join();
  17. }catch(Exception e){
  18. e.printStackTrace();
  19. }
  20. }
  21. }
  22. }
  23. }

守护线程和用户线程最关键的区别:Java虚拟机何时离开。只要在虚拟机上还有非守护线程在运行,那么虚拟机就不会离开。

如果只剩下守护线程了,那守护线程是为应用程序来服务的,这个时候当然也没什么必要存在了。从下面的代码中可以看到区别:

 1 class Worker extends Thread{
 2     public void run(){
 3         for(int i = 0; i < 1000000; i++){
 4             System.out.println("线程第"+i+"次执行");
 5         }
 6     }
 7 }
 8 public class Main{
 9     public static void main(String[] arg){
10         Worker a = new Worker();        
11         a.setDaemon(true);
12         a.start();
13         for(int i = 0; i < 20; i++){
14             System.out.println("主线程第"+i+"次执行");
15         }
16     }
17 }

同步的时候,不只是可以把一个对象当做是锁来使用,而且可以把一个方法声明为同步的,其实这样相当于把方法中的代码放入由该类型对应的Class对象来保护的。使用方法如下:

  1. class Foo{
  2. public String a = "";
  3. public synchronized void add(){
  4. a = a+"1";
  5. }
  6. }
  7. class Worker extends Thread{
  8. private Foo foo;
  9. public Worker(Foo foo){
  10. this.foo = foo;
  11. }
  12. public void run(){
  13. for(int i = 0; i < 100; i++){
  14. foo.add();
  15. }
  16. }
  17. }
  18. public class Main{
  19. public static void main(String[] arg){
  20. Foo foo = new Foo();
  21. Worker a = new Worker(foo);
  22. Worker b = new Worker(foo);
  23. a.start();
  24. b.start();
  25. try {
  26. a.join();
  27. b.join();
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. System.out.println(foo.a.length());
  32. }
  33. }

volatile只具有锁的可见性

只能在有限的一些情况下使用volatile变量代替锁,需要同时满足:对变量的写操作不依赖于当前值,该变量没有包含在有其他变量的不变式中。
在某些情况下,volatile变量同步机制的性能要优于锁。

它的特点是:只要值发生了改变,那么观察的线程都能读取到最新的值。

一个利用volatile的读取效率高和synchronized原子性的例子:

1 class CheesCounter{
2     private volatile int value;
3     public int getValue() {
4         return value;
5     }
6     public synchronized void setValue(int value) {
7         this.value = value;
8     }    
9 }

线程池的最简单最简单的例子,注意一下输出的线程的名称:

  1. public class Main {
  2. public static void main(String[] args) {
  3. ExecutorService pool = Executors.newFixedThreadPool(2);
  4. Thread t1 = new MyThread();
  5. Thread t2 = new MyThread();
  6. Thread t3 = new MyThread();
  7. Thread t4 = new MyThread();
  8. Thread t5 = new MyThread();
  9. pool.execute(t1);
  10. pool.execute(t2);
  11. pool.execute(t3);
  12. pool.execute(t4);
  13. pool.execute(t5);
  14. pool.shutdown();
  15. }
  16. }
  17. class MyThread extends Thread{
  18. public void run() {
  19. System.out.println(Thread.currentThread().getName()+"正在执行。。。");
  20. }
  21. }

线程池的延迟执行的功能:

 1 public class Main {
 2     public static void main(String[] args) {
 3         ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
 4         Thread t1 = new MyThread();
 5         Thread t2 = new MyThread();
 6         Thread t3 = new MyThread();
 7         Thread t4 = new MyThread();
 8         Thread t5 = new MyThread();
 9         pool.execute(t1);
10         pool.execute(t2);
11         pool.execute(t3);
12         pool.schedule(t4, 10000, TimeUnit.MILLISECONDS);
13         pool.schedule(t5, 10000, TimeUnit.MILLISECONDS);
14 
15         pool.shutdown();
16     }
17 }
18 class MyThread extends Thread {
19     public void run() {
20         System.out.println(Thread.currentThread().getName() + "正在执行。。。");
21     }
22 }

有返回值的线程:

  1. import java.util.concurrent.*;
  2. public class Main {
  3. public static void main(String[] args) throws ExecutionException, InterruptedException {
  4. ExecutorService pool = Executors.newFixedThreadPool(2);
  5. Callable c1 = new MyCallable("A");
  6. Callable c2 = new MyCallable("B");
  7. Future f1 = pool.submit(c1);
  8. Future f2 = pool.submit(c2);
  9. System.out.println(">>>"+f1.get().toString());
  10. System.out.println(">>>"+f2.get().toString());
  11. pool.shutdown();
  12. }
  13. }
  14. class MyCallable implements Callable{
  15. private String oid;
  16. MyCallable(String oid) {
  17. this.oid = oid;
  18. }
  19. public Object call() throws Exception {
  20. return oid+"任务返回的内容";
  21. }
  22. }

Lock用法的一个最简单的例子:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Foo{
    String str = "";
}
class Worker extends Thread{
    private Lock lock;
    private Foo foo;
    public Worker(Lock lock, Foo foo){
        this.lock = lock;
        this.foo = foo;
    }
    public void run() {        
        for(int i = 0; i < 1000; i++)
        {
            lock.lock();
            foo.str = foo.str + "1";
            lock.unlock();
        }
    }
}
public class Main{
    public static void main(String[] arg){
        Lock lock = new ReentrantLock();
        Foo foo = new Foo();
        Worker a = new Worker(lock, foo);
        Worker b = new Worker(lock, foo);
        a.start();
        b.start();
        try {
            a.join();
            b.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }        
        System.out.println(foo.str.length());
    }
}

下面是用ReadWriteLock来处理读者写者问题,封装之后相当简单了,但是没有C写的有意思:

  1. import java.util.concurrent.locks.ReadWriteLock;
  2. import java.util.concurrent.locks.ReentrantReadWriteLock;
  3. class Foo{
  4. String str = "";
  5. }
  6. class Reader extends Thread{
  7. Foo foo;
  8. ReadWriteLock lock;
  9. public Reader(Foo foo, ReadWriteLock lock){
  10. this.foo = foo;
  11. this.lock = lock;
  12. }
  13. public void run(){
  14. lock.readLock().lock();
  15. System.out.println("读者线程正在执行:"+Thread.currentThread().getName()+" 当前长度为:"+foo.str.length());
  16. try {
  17. Thread.sleep(1000);
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. }
  21. System.out.println("读者线程完成执行:"+Thread.currentThread().getName());
  22. lock.readLock().unlock();
  23. }
  24. }
  25. class Writer extends Thread{
  26. Foo foo;
  27. ReadWriteLock lock;
  28. public Writer(Foo foo, ReadWriteLock lock){
  29. this.foo = foo;
  30. this.lock = lock;
  31. }
  32. public void run(){
  33. lock.writeLock().lock();
  34. foo.str = foo.str + "0";
  35. System.out.println("写者线程正在执行:"+Thread.currentThread().getName()+" 当前长度为:"+foo.str.length());
  36. try {
  37. Thread.sleep(1000);
  38. } catch (InterruptedException e) {
  39. e.printStackTrace();
  40. }
  41. System.out.println("写者线程完成执行:"+Thread.currentThread().getName());
  42. lock.writeLock().unlock();
  43. }
  44. }
  45. public class Main{
  46. public static void main(String[] arg){
  47. ReadWriteLock lock = new ReentrantReadWriteLock(false);
  48. Foo foo = new Foo();
  49. for(int i = 0; i < 20; i++){
  50. if(i%4 == 0){
  51. Writer a = new Writer(foo, lock);
  52. a.start();
  53. }else{
  54. Reader a = new Reader(foo, lock);
  55. a.start();
  56. }
  57. }
  58. }
  59. }

初始化信号量的时候赋值为1,这样信号量就相当于一个锁了,下面是信号量最简单的例子:

 1 import java.util.concurrent.Semaphore;
 2 class Worker extends Thread{
 3     int x;
 4     Semaphore sp;
 5     public Worker(int x, Semaphore sp){
 6         this.x = x;
 7         this.sp = sp;
 8     }
 9     public void run(){
10         try {
11             sp.acquire(x);
12             System.out.println(Thread.currentThread().getName()+"成功获取"+x+"个信号量。");
13 
14             Thread.sleep(1000*x);
15             synchronized(Worker.class){
16                 sp.release(x);
17                 System.out.println(Thread.currentThread().getName()+"成功释放"+x+"个信号量。");
18             }
19         } catch (Exception e) {
20             e.printStackTrace();
21         }
22     }
23 }
24 public class Main{
25     public static void main(String[] arg){
26         Semaphore sp = new Semaphore(20);
27         for(int i = 0; i < 20; i++){
28             Worker a = new Worker(i%3+1, sp);
29             a.start();
30         }
31     }
32 }

BlockingQueue是阻塞队列,如果队列中满的时候put会将线程阻塞。

BlockingDeque是阻塞栈,如果栈中满的时候putFirst的时候会将线程阻塞。

条件变量Condition就是表示条件的一种变量,Java中的条件变量只能和锁配合使用来控制并发程序访问竞争资源的安全。

条件变量的出现是为了更精细地控制线程的等待和唤醒:一个锁可以有多个条件,每个条件上可以有多个线程等待,通过调用await方法可以让线程在该条件上等待,当调用signalAll方法,又可以唤醒该条件下的等待的线程。

下面是用法的最简单的一个例子,还没想到怎么演示与notify的实现的区别,以后再来补上:

  1. class Worker extends Thread{
  2. Condition condition;
  3. Lock lock;
  4. public Worker(Condition condition, Lock lock){
  5. this.condition = condition;
  6. this.lock = lock;
  7. }
  8. public void run(){
  9. lock.lock();
  10. System.out.println(Thread.currentThread().getName()+"正在运行。");
  11. condition.signalAll();
  12. lock.unlock();
  13. }
  14. }
  15. public class Main{
  16. public static void main(String[] arg){
  17. Lock lock = new ReentrantLock();
  18. Condition condition = lock.newCondition();
  19. ExecutorService pool = Executors.newFixedThreadPool(2);
  20. for(int i = 0; i < 10; i++){
  21. Worker a = new Worker(condition, lock);
  22. pool.execute(a);
  23. }
  24. pool.shutdown();
  25. }
  26. }

Java中也提供了就像C中的原子变量的类型,比如long对应的AtomicLong,其实也只是在单次的操作中保证不会因为多个线程并发地读写而造成值的错误,下面是一个测试的例子:

 1 class Foo{
 2     AtomicLong along = new AtomicLong(0);
 3 }
 4 class Worker extends Thread{
 5     Foo foo;
 6     public Worker(Foo foo){
 7         this.foo = foo;
 8     }
 9     public void run(){
10         foo.along.addAndGet(1);
11     }
12 }
13 public class Main{
14     public static void main(String[] arg){
15         Foo foo = new Foo();
16         for(int i = 0; i < 10000; i++){
17             Worker a = new Worker(foo);
18             a.start();
19             try {
20                 a.join();
21             } catch (InterruptedException e) {
22                 e.printStackTrace();
23             }
24         }
25         System.out.println(foo.along.get());
26     }
27 }

有时候一个大的任务,常常需要分配给多个子任务区执行,只有当所有的子任务都执行完成的时候才能执行主任务,一个简单的例子,感觉和join有点像:

  1. public class Main {
  2. public static void main(String[] args) {
  3. CyclicBarrier cb = new CyclicBarrier (10, new MainTask());
  4. for(int i = 0; i < 10; i++){
  5. new SubTask(cb).start();
  6. }
  7. }
  8. }
  9. class MainTask implements Runnable {
  10. public void run() {
  11. System.out.println("主线程");
  12. }
  13. }
  14. class SubTask extends Thread {
  15. private CyclicBarrier cb;
  16. SubTask(CyclicBarrier cb) {
  17. this.cb = cb;
  18. }
  19. public void run() {
  20. try {
  21. Thread.sleep(1000);
  22. System.out.println("[子任务" + Thread.currentThread().getName() + "]完成,并通知障碍器已经完成!");
  23. cb.await();
  24. } catch (Exception e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. }

可以看到是子线程都执行了await之后MainTask才会执行。如果没有给定数目的子任务,那么主线程就永远不会运行了。

上面是看到网上的例子,自己实践了一下多线程的用法,都很浅显,就是笔记。关于深入的理解会在后面的博客中给出。

分享到:
评论

相关推荐

    Java多线程知识点总结

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

    java 多线程操作数据库

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

    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多线程的讲解和实战

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

    java多线程分页查询

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

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

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

    java多线程ppt

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

    java多线程读取文件

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

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

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

    深入浅出 Java 多线程.pdf

    在本文中,我们将深入浅出Java多线程编程的世界,探索多线程编程的基本概念、多线程编程的优点、多线程编程的缺点、多线程编程的应用场景、多线程编程的实现方法等内容。 一、多线程编程的基本概念 多线程编程是指...

    java 多线程并发实例

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

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

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

    java多线程实现大批量数据导入源码

    本项目以"java多线程实现大批量数据导入源码"为题,旨在通过多线程策略将大量数据切分,并进行并行处理,以提高数据处理速度。 首先,我们需要理解Java中的线程机制。Java通过`Thread`类来创建和管理线程。每个线程...

    java多线程查询数据库

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

    java 多线程编程实战指南(核心 + 设计模式 完整版)

    《Java多线程编程实战指南》这本书深入浅出地讲解了Java多线程的核心概念和实战技巧,分为核心篇和设计模式篇,旨在帮助开发者掌握并应用多线程技术。 1. **线程基础** - **线程的创建**:Java提供了两种创建线程...

    JAVA多线程编程技术PDF

    这份“JAVA多线程编程技术PDF”是学习和掌握这一领域的经典资料,涵盖了多线程的全部知识点。 首先,多线程的核心概念包括线程的创建与启动。在Java中,可以通过实现Runnable接口或继承Thread类来创建线程。创建后...

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

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

    Java多线程编程实战指南(核心篇)

    Java多线程编程实战指南(核心篇) 高清pdf带目录 随着现代处理器的生产工艺从提升处理器主频频率转向多核化,即在一块芯片上集成多个处理器内核(Core),多核处理器(Multicore Processor)离我们越来越近了――如今...

Global site tag (gtag.js) - Google Analytics