`
wx1568847608
  • 浏览: 12246 次
文章分类
社区版块
存档分类
最新评论

Java的线程同步和并发问题示例

 
阅读更多

并发问题

多线程是一个非常强大的工具,它使我们能够更好地利用系统的资源,但我们需要在读取和写入多个线程共享的数据时特别小心。

当多个线程尝试同时读取和写入共享数据时,会出现两种类型的问题 -

线程干扰错误内存一致性错误让我们逐一理解这些问题。

线程干扰错误(竞争条件)

考虑以下Counter类,其中包含一个increment()方法,每次调用它时计数增加一次 -

u=339982189,4031796210&fm=173&app=49&f=JPEG?w=413&h=241&s=E8E0A1401BE4877C4C51140F0000E0C0

现在,让我们假设几个线程试图通过increment()同时调用方法来增加计数-

u=2045823439,3050123039&fm=173&app=49&f=JPEG?w=617&h=429&s=E8F2A3425BAC92490ED5C403000070C0

您认为上述计划的结果如何?最终计数是1000,因为我们调用增量1000次?

但实际上答案是否定的!只需运行上面的程序,自己查看输出。它不是产生最终计数1000,而是每次运行时都会产生不一致的结果。我在计算机上运行了上述程序三次,输出为992,996和993。

让我们深入研究该程序并理解程序输出不一致的原因 -

当线程执行increment()方法时,执行以下三个步骤:1。检索计数的当前值2.将检索的值增加1 3.将增加的值重新存储到计数中

现在让我们假设两个线程 - ThreadA和ThreadB按以下顺序执行这些操作 -

ThreadA:检索计数,初始值= 0ThreadB:检索计数,初始值= 0ThreadA:增加检索值,结果= 1ThreadB:增加检索值,结果= 1ThreadA:存储递增的值,count现在为1ThreadB:存储递增的值,count现在为1两个线程都尝试将计数递增1,但最终结果是1而不是2,因为线程执行的操作相互交错。在上述情况下,ThreadA完成的更新将丢失。

上述执行顺序只是一种可能性。可能有许多这样的命令可以执行这些操作,使程序的输出不一致。

当多个线程尝试同时读取和写入共享变量,并且这些读取和写入操作在执行中重叠时,最终结果取决于读取和写入发生的顺序,这是不可预测的。这种现象称为种族状况。

访问共享变量的代码部分称为Critical Section。

通过同步对共享变量的访问可以避免线程干扰错误。

让我们首先看一下多线程程序中出现的第二种错误 - 内存一致性错误。

内存一致性错误

当不同的线程具有相同数据的不一致视图时,会发生内存不一致错误。当一个线程更新某些共享数据时会发生这种情况,但此更新不会传播到其他线程,并且最终会使用旧数据。

为什么会这样?嗯,这可能有很多原因。编译器会对您的程序进行多次优化以提高性能。它还可能重新排序指令以优化性能。处理器也尝试优化事物,例如,处理器可能从临时寄存器(包含变量的最后读取值)读取变量的当前值,而不是主存储器(具有变量的最新值) 。

请考虑以下示例,该示例演示了操作中的内存一致性错误 -

u=826939650,1721147994&fm=173&app=49&f=JPEG?w=640&h=572&s=E8E0A3425BA4B36C4E55CC0F0000E0C0

在理想情况下,上述计划应 -

等待一秒钟,然后打印Hello World!后sayHello变为真。等待一秒钟,然后打印Good Bye!后sayHello变为假。

u=1919704644,2062309291&fm=173&app=49&f=JPEG?w=229&h=118&s=ACF2E3021BE4B65B1ED1980F0000E0C0

但是在运行上述程序后我们是否得到了所需的输出?好吧,如果你运行程序,你会看到以下输出 -

此外,该程序甚至没有终止。

线程等待。什么?怎么可能?

是! 这就是内存一致性错误。第一个线程不知道主线程对sayHello变量所做的更改。

您可以使用volatile关键字来避免内存一致性错误。我们很快就会详细了解volatile关键字。

同步

通过确保以下两件事可以避免线程干扰和内存一致性错误 -

一次只有一个线程可以读写共享变量。当一个线程正在访问共享变量时,其他线程应该等到第一个线程完成。这保证了对共享变量的访问是Atomic,并且多个线程不会干扰。每当任何线程修改共享变量时,它都会自动建立与其他线程后续读取和写入共享变量的先发生关系。这可以保证一个线程所做的更改对其他人可见。幸运的是,Java有一个synchronized关键字,您可以使用该关键字同步对任何共享资源的访问,从而避免这两种错误。

同步方法

以下是Counter类的同步版本。我们synchronized在increment()方法上使用Java的关键字来防止多个线程同时访问它 -

u=2622912749,567519848&fm=173&app=49&f=JPEG?w=640&h=335&s=68C2B3401FE0936A0C49600F0000E0C0

u=1370763100,148721422&fm=173&app=49&f=JPEG?w=640&h=331&s=68F2A34497F884694E5554060000E0C0

如果运行上述程序,它将产生1000的所需输出。不会出现竞争条件,并且最终输出始终保持一致。该synchronized关键字可确保只有一个线程可以进入increment()一次的方法。

请注意,同步的概念始终绑定到对象。在上面的例子中,increment()在同一个实例上多次调用方法SynchonizedCounter会导致竞争条件。我们正在使用synchronized关键字防范这种情况。但是线程可以安全地increment()在不同的实例上SynchronizedCounter同时调用方法,这不会导致竞争条件。

在静态方法的情况下,同步与Class对象相关联。

同步代码块

Java内部使用所谓的内部锁或监视器锁来管理线程同步。每个对象都有一个与之关联的内在锁。

当一个线程调用一个对象的synchronized方法时,它会自动获取该对象的内部锁,并在该方法退出时释放它。即使方法抛出异常,也会发生锁定释放。

在静态方法的情况下,线程获取Class与类关联的对象的内部锁,这与该类的任何实例的内部锁不同。

synchronizedkeyword也可以用作块语句,但与synchronized方法不同,synchronized语句必须指定提供内部锁的对象 -

u=308561562,1001143586&fm=173&app=49&f=JPEG?w=295&h=201&s=20C0B34AD3F4B66E14E5450B0000E0C0

 

当线程获取对象的内部锁时,其他线程必须等到锁被释放。但是,当前拥有锁的线程可以多次获取它而没有任何问题。

允许线程多次获取同一个锁的想法称为“ 重入同步”。

易变的关键字

Volatile关键字用于避免多线程程序中的内存一致性错误。它告诉编译器避免对变量进行任何优化。如果将变量标记为volatile,则编译器不会优化或重新排序该变量的指令。

此外,变量的值将始终从主存储器而不是临时寄存器中读取。

以下是我们在上一节中看到的相同MemoryConsistencyError示例,不同之处在于,这次我们sayHello使用volatile关键字标记了变量。

u=814303887,1193966924&fm=173&app=49&f=JPEG?w=548&h=595&s=E8E0A34053B4B26F4EC55C0F0000E0C0

 

运行上述程序会产生所需的输出 -

u=4228634948,1683536884&fm=173&app=49&f=JPEG?w=218&h=103&s=ACE2E3021BF0945B1ED1900F0000E0C0

 

结论

通过示例我们了解了多线程程序中可能出现的不同并发问题以及如何使用synchronized方法和块来避免它们。同步是一个强大的工具,但请注意,不必要的同步可能会导致其他问题,如死锁和饥饿。

 

一、线程并发同步概念

线程同步其核心就在于一个“同”。所谓“同”就是协同、协助、配合,“同步”就是协同步调昨,也就是按照预定的先后顺序进行运行,即“你先,我等, 你做完,我再做”。

线程同步,就是当线程发出一个功能调用时,在没有得到结果之前,该调用就不会返回,其他线程也不能调用该方法。

就一般而言,我们在说同步、异步的时候,特指那些需要其他组件来配合或者需要一定时间来完成的任务。在多线程编程里面,一些较为敏感的数据时不允许被多个线程同时访问的,使用线程同步技术,确保数据在任何时刻最多只有一个线程访问,保证数据的完整性。

二、线程同步中可能存在安全隐患

用生活中的场景来举例:小生去银行开个银行账户,银行给 me 一张银行卡和一张存折,小生用银行卡和存折来搞事情:

银行卡疯狂存钱,存完一次就看一下余额;同时用存折子不停地取钱,取一次钱就看一下余额;

具体代码实现如下:

先弄一个银行账户对象,封装了存取插钱的方法:

复制代码

1 package com.test.threadDemo2;
 2 
 3 /**
 4  * 银行账户
 5  * @author Administrator
 6  *
 7  */
 8 public class Acount {
 9     private int count=0;
10     
11     /**
12      * 存钱
13      * @param money
14      */
15     public void addAcount(String name,int money) {
16         17             // 存钱
18             count += money;
19             System.out.println(name+"...存入:"+money+"..."+Thread.currentThread().getName());
20             SelectAcount(name);
21 22     }
23     
24     /**
25      * 取钱
26      * @param money
27      */
28     public void subAcount(String name,int money) {
29         30             // 先判断账户现在的余额是否够取钱金额
31             if(count-money < 0){  
32                 System.out.println("账户余额不足!"); 
33                 return;  
34             } 
35             // 取钱
36             count -= money;
37             System.out.println(name+"...取出:"+money+"..."+Thread.currentThread().getName());
38             SelectAcount(name);
39 40     }
41     
42     /**
43      * 查询余额
44      */
45     public void SelectAcount(String name) {
46         System.out.println(name+"...余额:"+count);
47     }
48 }

复制代码

编写银行卡对象:

复制代码

1 package com.test.threadDemo2;
 2 /**
 3  * 银行卡负责存钱
 4  * @author Administrator
 5  *
 6  */
 7 public class Card implements Runnable{
 8     private String name;
 9     private Account account = new Account();
10     
11     public Card(String name,Account account) {
12         this.account = account;
13         this.name = name;
14     }
15     
16     @Override
17     public void run() {
18         
19         while(true) {
20             try {
21                 Thread.sleep(1000);
22             } catch (InterruptedException e) {
23                 e.printStackTrace();
24             }
25             account.addAccount(name,100);26         }
27     }
28     
29 }

复制代码

编写存折对象(和银行卡方法几乎一模一样,就是名字不同而已):

复制代码

package com.test.threadDemo2;
/**
 * 存折负责取钱
 * @author Administrator
 *
 */
public class Paper implements Runnable{
    private String name;
    private Account account = new Account();
    
    public Paper(String name,Account account) {
        this.account = account;
        this.name = name;
    }
    
    @Override
    public void run() {
        while(true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            account.subAccount(name,50);
        }
        
    }

}

复制代码

主方法测试,演示银行卡疯狂存钱,存折疯狂取钱:

复制代码

1 package com.test.threadDemo2;
 2 
 3 public class ThreadDemo2 {
 4     public static void main(String[] args) {
 5         
 6         // 开个银行帐号
 7         Account account = new Account();
 8         // 开银行帐号之后银行给张银行卡
 9         Card card = new Card("card",account);
10         // 开银行帐号之后银行给张存折
11         Paper paper = new Paper("存折",account);
12         
13         Thread thread1 = new Thread(card);
14         Thread thread2 = new Thread(paper);
15         
16         thread1.start();
17         thread2.start();            
18     }
19 }

复制代码

结果显示:从中可以看出 bug

 

从上面的例子里就可以看出,银行卡存钱和存折取钱的过程中使用了 sleep() 方法,这只不过是小生模拟“系统卡顿”现象:银行卡存钱之后,还没来得及查余额,存折就在取钱,刚取完钱,银行卡这边“卡顿”又好了,查询一下余额,发现钱存的数量不对!当然还有“卡顿”时间比较长,存折在卡顿的过程中,把钱全取了,等银行卡这边“卡顿”好了,一查发现钱全没了的情况可能。

因此多个线程一起访问共享的数据的时候,就会可能出现数据不同步的问题,本来一个存钱的时候不允许别人打断我(当然实际中可以存在刚存就被取了,有交易记录在,无论怎么动这个帐号,都是自己的银行卡和存折在动钱。小生这个例子里,要求的是存钱和查钱是一个完整过程,不可以拆分开),但从结果来看,并没有实现小生想要出现的效果,这破坏了线程“原子性”。

三、线程同步中可能存在安全隐患的解决方法

  从上面的例子中可以看出线程同步中存在安全隐患,我们必须不能忽略,所以要引入“锁”(术语叫监听器)的概念:

3.1 同步代码块:

  使用 synchronized() 对需要完整执行的语句进行“包裹”,synchronized(Obj obj) 构造方法里是可以传入任何类的对象,

  但是既然是监听器就传一个唯一的对象来保证“锁”的唯一性,因此一般使用共享资源的对象来作为 obj 传入 synchronized(Obj obj) 里:

  只需要锁 Account 类中的存钱取钱方法就行了:

复制代码

package com.test.threadDemo2;

/**
 * 银行账户
 * @author Administrator
 *
 */
public class Acount {
    private int count=0;
    
    /**
     * 存钱
     * @param money
     */
    public void addAcount(String name,int money) {
        synchronized(this) {
            // 存钱
            count += money;
            System.out.println(name+"...存入:"+money+"..."+Thread.currentThread().getName());
            SelectAcount(name);
        }
    }
    
    /**
     * 取钱
     * @param money
     */
    public void subAcount(String name,int money) {
        synchronized(this) {
            // 先判断账户现在的余额是否够取钱金额
            if(count-money < 0){  
                System.out.println("账户余额不足!"); 
                return;  
            } 
            // 取钱
            count -= money;
            System.out.println(name+"...取出:"+money+"..."+Thread.currentThread().getName());
            SelectAcount(name);
        }
    }
    
    /**
     * 查询余额
     */
    public void SelectAcount(String name) {
        System.out.println(name+"...余额:"+count);
    }
}

复制代码

3.2 同步方法

者在方法的申明里申明 synchronized 即可:

复制代码

1 package com.test.threadDemo2;
 2 /**
 3  * 银行账户
 4  * @author Administrator
 5  *
 6  */
 7 public class Acount {
 8     private int count;
 9     
10     /**
11      * 存钱
12      * @param money
13      */
14     public synchronized void addAcount(String name,int money) {
15             // 存钱
16             count += money;
17             System.out.println(name+"...存入:"+money);
18     }
19     
20     /**
21      * 取钱
22      * @param money
23      */
24     public synchronized void subAcount(String name,int money) {
25             // 先判断账户现在的余额是否够取钱金额
26             if(count-money < 0){  
27                 System.out.println("账户余额不足!");  
28                 return;  
29             } 
30             // 取钱
31             count -= money;
32             System.out.println(name+"...取出:"+money);
33     }
34     
35     /**
36      * 查询余额
37      */
38     public void SelectAcount(String name) {
39         System.out.println(name+"...余额:"+count);
40     }
41 }

复制代码

运行效果:

3.3 使用同步锁:

account 类创建私有的 ReetrantLock 对象,调用 lock() 方法,同步执行体执行完毕之后,需要用 unlock() 释放锁。

复制代码

package com.test.threadDemo2;

import java.util.concurrent.locks.ReentrantLock;

/**
 * 银行账户
 * @author Administrator
 *
 */
public class Acount {
    private int count;
    private ReentrantLock lock = new ReentrantLock();
    
    /**
     * 存钱
     * @param money
     */
    public void addAcount(String name,int money) {
        lock.lock();
        try{
            // 存钱
            count += money;
            System.out.println(name+"...存入:"+money);
        }finally {
            lock.unlock();
        }
    }
    
    /**
     * 取钱
     * @param money
     */
    public void subAcount(String name,int money) {
        lock.lock();
        try{
            // 先判断账户现在的余额是否够取钱金额
            if(count-money < 0){  
                System.out.println("账户余额不足!");  
                return;  
            } 
            // 取钱
            count -= money;
            System.out.println(name+"...取出:"+money);
        }finally {
            lock.unlock();
        }
    }
    
    /**
     * 查询余额
     */
    public void SelectAcount(String name) {
        System.out.println(name+"...余额:"+count);
    }
}

复制代码

运行效果: 

 

 四、死锁

当线程需要同时持有多个锁时,有可能产生死锁。考虑如下情形:

线程 A 当前持有互斥所锁 lock1,线程 B 当前持有互斥锁 lock2。

接下来,当线程 A 仍然持有 lock1 时,它试图获取 lock2,因为线程 B 正持有 lock2,因此线程 A 会阻塞等待线程 B 对 lock2 的释放。

如果此时线程 B 在持有 lock2 的时候,也在试图获取 lock1,因为线程 A 正持有 lock1,因此线程 B 会阻塞等待 A 对 lock1 的释放。

二者都在等待对方所持有锁的释放,而二者却又都没释放自己所持有的锁,这时二者便会一直阻塞下去。这种情形称为死锁。

复制代码

1 package com.testDeadLockDemo;
2 
3 public class LockA {
4     
5     private LockA(){}
6     
7     public static final LockA lockA = new LockA();
8 }

复制代码

复制代码

1 package com.testDeadLockDemo;
2 
3 public class LockB {
4     private LockB(){}
5     
6     public static final LockB lockB = new LockB();
7 }

复制代码

复制代码

package com.testDeadLockDemo;

public class DeadLock implements Runnable{
    private int i=0;
    
    @Override
    public void run() {
        while(true) {
            if(i%2==0){
                synchronized(LockA.lockA) {
                    System.out.println("if...lockA");
                    synchronized(LockB.lockB) {
                        System.out.println("if...lockB");
                    }
                }
            }else {
                synchronized(LockB.lockB) {
                    System.out.println("else...lockB");
                    synchronized(LockA.lockA) {
                        System.out.println("else...lockA");
                    }
                }
            }
            i++;
        }
        
    }
}

复制代码

测试:

复制代码

1 package com.testDeadLockDemo;
 2 
 3 public class Test {
 4     public static void main(String[] args) {
 5         DeadLock deadLock = new DeadLock();
 6         
 7         Thread t1 = new Thread(deadLock);
 8         Thread t2 = new Thread(deadLock);
 9         t1.start();
10         t2.start();
11         
12     }
13 }

复制代码

运行结果:

 五、线程通信

在共享资源中增加镖旗,当镖旗为真的时候才可以存钱,存完了就把镖旗设置成假,当取款的时候发现镖旗为假的时候,可以取款,取完款就把镖旗设置为真。

只需修改 Account 类 和 测试类 即可

复制代码

1 package com.test.threadDemo2;
 2 
 3 /**
 4  * 银行账户
 5  * @author Administrator
 6  *
 7  */
 8 public class Acount {
 9     private boolean flag=false;    // 默认flag 为false,要求必须先存款再取款
10     private int count=0;
11     
12     /**
13      * 存钱
14      * @param money
15      */
16     public void addAcount(String name,int money) {
17         synchronized(this) {
18             // flag 为true 表示可以存款,否则不可以存款
19             if(flag) {
20                 try {
21                     this.wait();
22                 } catch (InterruptedException e) {
23                     // TODO Auto-generated catch block
24                     e.printStackTrace();
25                 }
26             }else {
27                 // 存钱
28                 count += money;
29                 System.out.println(name+"...存入:"+money+"..."+Thread.currentThread().getName());
30                 SelectAcount(name);
31                 flag = true;
32                 this.notifyAll();
33             }
34         }
35     }
36     
37     /**
38      * 取钱
39      * @param money
40      */
41     public void subAcount(String name,int money) {
42         synchronized(this) {
43             if(!flag) {
44                 try {
45                     this.wait();
46                 } catch (InterruptedException e) {
47                     e.printStackTrace();
48                 }
49             }else {
50             // 先判断账户现在的余额是否够取钱金额
51             if(count-money < 0){  
52                 System.out.println("账户余额不足!"); 
53                 return;  
54             } 
55                 // 取钱
56                 count -= money;
57                 System.out.println(name+"...取出:"+money+"..."+Thread.currentThread().getName());
58                 SelectAcount(name);
59                 flag = false;
60                 this.notifyAll();
61             }
62         }
63     }
64     
65     /**
66      * 查询余额
67      */
68     public void SelectAcount(String name) {
69         System.out.println(name+"...余额:"+count);
70     }
71 }

复制代码

复制代码

1 package com.test.threadDemo2;
 2 
 3 public class ThreadDemo2 {
 4     public static void main(String[] args) {
 5         
 6         // 开个银行帐号
 7         Acount acount = new Acount();
 8         
 9         // 开银行帐号之后银行给张银行卡
10         Card card1 = new Card("card1",acount);
11         Card card2 = new Card("card2",acount);
12         Card card3 = new Card("card3",acount);
13         
14         // 开银行帐号之后银行给张存折
15         Paper paper1 = new Paper("paper1",acount);
16         Paper paper2 = new Paper("paper2",acount);
17         
18         // 创建三个银行卡
19         Thread thread1 = new Thread(card1,"card1");
20         Thread thread2 = new Thread(card2,"card2");
21         Thread thread3 = new Thread(card3,"card3");
22         // 创建两个存折
23         Thread thread4 = new Thread(paper1,"paper1");
24         Thread thread5 = new Thread(paper2,"paper2");
25         
26         thread1.start();
27         thread2.start();
28         thread3.start();
29         
30         thread4.start();
31         thread5.start();
32     }
33 }

复制代码

运行结果:

 使用同步锁也可以达到相同的目的:

复制代码

package com.test.threadDemo2;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 银行账户
 * @author Administrator
 *
 */
public class Acount2 {
    private boolean flag=false;    // 默认flag 为false,要求必须先存款再取款
    private int count=0;
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    
    /**
     * 存钱
     * @param money
     */
    public void addAcount(String name,int money) {
        lock.lock();
        try {
            // flag 为true 表示可以存款,否则不可以存款
            if(flag) {
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }else {
                // 存钱
                count += money;
                System.out.println(name+"...存入:"+money+"..."+Thread.currentThread().getName());
                SelectAcount(name);
                flag = true;
                condition.signalAll();
            }
        }finally {
            lock.unlock();
        }
    }
    
    /**
     * 取钱
     * @param money
     */
    public void subAcount(String name,int money) {
        lock.lock();
        try {
            if(!flag) {
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else {
            // 先判断账户现在的余额是否够取钱金额
            if(count-money < 0){  
                System.out.println("账户余额不足!"); 
                return;  
            } 
                // 取钱
                count -= money;
                System.out.println(name+"...取出:"+money+"..."+Thread.currentThread().getName());
                SelectAcount(name);
                flag = false;
                condition.signalAll();
            }
        }finally {
            lock.unlock();
        }
    }
    
    /**
     * 查询余额
     */
    public void SelectAcount(String name) {
        System.out.println(name+"...余额:"+count);
    }
}

 

 

 

« 上一篇:【java多线程】多线程的创建三种方式--

转载于:https://my.oschina.net/u/3043570/blog/3063198

分享到:
评论

相关推荐

    Java多线程和并发知识整理

    1.3并发问题的根源 1.4JMM 1.5线程安全的分类 1.6线程安全的方法 二、线程基础 2.1状态 2.2使用方式 2.3基础机制 2.4中断 2.5互斥同步 2.6线程合作 三、Synchronized 详解 3.1 使用 3.2 原理分析 3.3 ...

    Java并发编程相关源码集 包括多任务线程,线程池等.rar

    多个线程竞争问题、多个线程多个锁问题、创建一个缓存的线程池、多线程使用Vector或者HashTable的示例(简单线程同步问题)、PriorityBlockingQueue示例、高性能无阻塞无界队列: ConcurrentLinkedQueue、DelayQueue...

    Java并发编程实战

    5.1.1 同步容器类的问题 5.1.2 迭代器与Concurrent-ModificationException 5.1.3 隐藏迭代器 5.2 并发容器 5.2.1 ConcurrentHashMap 5.2.2 额外的原子Map操作 5.2.3 CopyOnWriteArrayList 5.3 阻塞队列和...

    基于Java多线程与线程安全实践(源码+使用文档)

    线程安全示例:提供一系列的Java线程安全编程示例,包括同步代码块、同步方法、以及使用锁等。 并发集合操作:展示如何在多线程环境下安全地操作集合类,如ConcurrentHashMap和CopyOnWriteArrayList。 线程生命周期...

    java线程并发semaphore类示例

    Java 5.0里新加了4个协调线程间进程的同步装置,它们分别是Semaphore, CountDownLatch, CyclicBarrier和Exchanger,本例主要介绍Semaphore,Semaphore是用来管理一个资源池的工具,可以看成是个通行证

    Java有两种实现多线程的方式:通过Runnable接口、通过Thread直接实现,请掌握这两种实现方式,并编写示例程序。

    一、实验目的 掌握多线程程序设计 二、实验环境 1、微型计算机一台 2、WINDOWS操作系统,Java SDK,Eclipse开发环境 三、实验内容 ...3、采用线程同步方法机制来解决多线程共享冲突问题,编写示例程序。

    java中的并发和多线程编程中文版

    读者将通过使用java.lang.thread类、synchronized和volatile关键字,以及wait、notify和notifyall方法,学习如何初始化、控制和协调并发操作。此外,本书还提供了有关并发编程的全方位的详细内容,例如限制和同步、...

    Java 并发编程实战

    5.1.1 同步容器类的问题 5.1.2 迭代器与Concurrent-ModificationException 5.1.3 隐藏迭代器 5.2 并发容器 5.2.1 ConcurrentHashMap 5.2.2 额外的原子Map操作 5.2.3 CopyOnWriteArrayList 5.3 阻塞队列和...

    《Java并发编程实战》

    《Java并发编程实战》深入浅出地介绍了Java线程和并发,是一本完美的Java并发参考手册。书中从并发性和线程安全性的基本概念出发,介绍了如何使用类库提供的基本并发构建块,用于避免并发危险、构造线程安全的类及验证...

    java并发编程

    此外,本书还提供了有关并发编程的全方位的详细内容,例如限制和同步、死锁和冲突、依赖于状态的操作控制、异步消息传递和控制流、协作交互,以及如何创建基于web的服务和计算型服务。 本书的读者对象是那些希望掌握...

    Java虚拟机并发编程

    第二部分:现代java/jdk并发,讨论了现代java api的线程安全和效率,以及如何处理已有应用程序中的现实问题和重构遗留代码时的原则;第三部分:软件事务内存,深入讨论了stm并就如何在各种主要的jvm语言里使用stm给...

    Java并发之传统线程同步通信技术代码详解

    主要介绍了Java并发之传统线程同步通信技术代码详解,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下

    Java 语言教程.docx.docx

    本章节讲解了如何创建和管理多线程程序,描述了线程同步和并发编程的基本概念。 常用类库:介绍Java标准库中的一些常用类和实用工具,如 ArrayList、HashMap、文件I/O操作和网络编程等。帮助开发者高效地完成常见的...

    java并发编程专题(三)----详解线程的同步

    主要介绍了JAVA并发编程 线程同步的的相关资料,文中示例代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下

    Java面试通关宝典:深度解读核心知识点与实战技巧,全面提升面试表现力与技术实力

    多线程与并发:这部分问题涉及到Java的线程模型、同步机制、并发工具类等。 JVM与性能优化:这部分问题涵盖了JVM内存模型、垃圾收集、性能调优等内容。例如,解释JVM的内存区域划分和作用;理解垃圾收集算法和调优...

    java版飞机大战源码-concurrency-practice:并发实践

    这是Java并发研究的书籍和示例集合,还有源码姊妹篇源码分析集合。研究基于操作系统层,语言层,应用层。 操作系统层主要参考进程管理(进程调度,同步):《深入理解计算机系统》,《操作系统概念》 语言层主要依赖于...

    java开源包4

    MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包 jSIP.tar jSIP这个Java包目标是用Java实现SIP(SIP:...

    java开源包11

    MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包 jSIP.tar jSIP这个Java包目标是用Java实现SIP(SIP:...

    java开源包6

    MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包 jSIP.tar jSIP这个Java包目标是用Java实现SIP(SIP:...

Global site tag (gtag.js) - Google Analytics