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

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多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式...

    java多线程读取文件

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

    java多线程ppt

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

    java 多线程操作数据库

    一个java 多线程操作数据库应用程序!!!

    java多线程经典案例

    java多线程经典案例,线程同步、线程通信、线程阻塞等经典案例

    Java多线程编程技术

    《Java多线程编程核心技术》建议猿友们读两遍,因为其写得没有那么抽象,第一遍有些概念不是很理解,可以先跳过并记录起来,第一遍阅读的目的主要是了解整个架构。第二遍再慢慢品味,并贯穿全部是指点来思考,并将...

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

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

    Java多线程知识点总结

    该文档总结了Java多线程相关的知识点,分享给大家,简单易懂!

    java多线程的讲解和实战

    详细的讲解了java多线程的原理,并配有代码进行实战,适合java初学者和想对多线程有进一步了解的人。

    java多线程通信图解

    一张图方便理解和掌握java 多线程之间通信的实质 java 多线程 其实就是每个线程都拥有自己的内存空间,多线程之间的通信,比例A线程修改了主内存(main方法的线程)变量,需要把A线程修改的结果同步到主线程中,...

    java多线程处理数据库数据

    java多线程处理数据库数据,使用并发包,无框架,可批量处数据库数据,进行增删改。。等等操作。

    java多线程,对多线程,线程池进行封装,方便使用

    java多线程,对多线程,线程池进行封装,方便使用

    Java多线程编程经验

    现在的操作系统是多任务操作系统。多线程是实现多任务的一种方式。 线程是指进程中的一个执行流程,一个进程中可以运行多个线程。...本文档提供Java多线程编程经验,方便广大Java爱好者研究学习Java多线程

    java多线程处理大数据

    java多线程处理大数据,可根据配置的线程数,任务去调度处理

    java多线程并发

    java多线程并发的在新窗口

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

    Java多线程机制 9.1 Java中的线程 9.2 Thread的子类创建线程 9.3 使用Runable接口 9.4 线程的常用方法 9.5 GUI线程 9.6 线程同步 9.7 在同步方法中使用wait()、notify 和notifyAll()方法 9.8 挂起、恢复和终止线程 ...

    java多线程核心技术

    资深Java专家10年经验总结,全程案例式讲解,首本全面介绍Java多线程编程技术的专著 结合大量实例,全面讲解Java多线程编程中的并发访问、线程间通信、锁等最难突破的核心技术与应用实践 Java多线程无处不在,如...

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

    java多线程实现大批量数据切分成指定份数的数据,然后多线程处理入库或者导出,线程的个数和每份数据的数量都可以控制

    java多线程查询数据库

    java多线程并发查询数据库,使用线程池控制分页,并发查询。

    java多线程模拟队列实现排队叫号

    java多线程模拟队列实现排队叫号,多线程模拟排队叫号取号 java多线程模拟队列实现排队叫号,多线程模拟排队叫号取号

Global site tag (gtag.js) - Google Analytics