Java 多线程测试 笔记(一)
测试 没有Synchronized的并发 结果
用比较实际的方式测试,比如说卖东西,赚钱
public class Sell implements Runnable {
static Sell sell = new Sell();
//商品总数
static int num = 500;
//收到的钱 , 每件10块钱
static BigDecimal money = new BigDecimal(0);
//给营业员 , 每件奖励1块钱
static AtomicInteger reward = new AtomicInteger(0);
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
num--;
reward.getAndIncrement();
money = money.add(new BigDecimal(10));
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(sell);
Thread t2 = new Thread(sell);
t1.start();
t2.start();
System.out.println("剩余数量:" + num);
System.out.println("赚到的钱:" + money);
System.out.println("营业员钱:" + reward);
}
}
理想情况下卖500件商品,应该可以赚到5000块钱,没有任何的措施,多次运行得到的结果如下:
剩余数量:489
赚到的钱:2170
营业员钱:309
----------------------------------------
剩余数量:485
赚到的钱:1770
营业员钱:286
----------------------------------------
剩余数量:-335
赚到的钱:10950
营业员钱:869
----------------------------------------
发现,卖出的商品和赚到的钱完全不成正比,但是商家应该会很开心,钱多了
用最简单的num-- 说明: num--包含三个操作
1.读取num
2.num-1
3.num值写入内存
当t1线程进行1,2步操作时,还未写入内存时,t2线程进入现场,读取得到的num将会是原值,所以造成了线程不安全的问题
为了数据安全加点措施
加个判断如果商品数量大于0,就继续卖钱,并在t1,t2线程,调用join()方法
Thread.join() 官方的解释
public final void join() throws InterruptedException Waits for this thread to die. Throws: InterruptedException - if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.
即join()的作用是:“等待该线程终止”,这里需要理解的就是该线程是指的主线程等待子线程的终止. 也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行
说明: 主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了
public class Sell implements Runnable {
static Sell sell = new Sell();
//商品总数
static int num = 500;
//收到的钱 , 每件10块钱
static BigDecimal money = new BigDecimal(0);
//给营业员 , 每件奖励1块钱
static AtomicInteger reward = new AtomicInteger(0);
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
num--;
reward.getAndIncrement();
money = money.add(new BigDecimal(10));
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(sell);
Thread t2 = new Thread(sell);
t1.start();
t1.join();
t2.start();
t2.join();
System.out.println("剩余数量:" + num);
System.out.println("赚到的钱:" + money);
System.out.println("营业员钱:" + reward);
}
}
得到稳定的结果 :
剩余数量:-1500
赚到的钱:20000
营业员钱:2000
数量是对的,但是透支库存了,所以通俗的在run()里,加入if判断
public class Sell implements Runnable {
static Sell sell = new Sell();
//商品总数
static int num = 500;
//收到的钱 , 每件10块钱
static BigDecimal money = new BigDecimal(0);
//给营业员 , 每件奖励1块钱
static AtomicInteger reward = new AtomicInteger(0);
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
if (num > 0) {
num--;
reward.getAndIncrement();
money = money.add(new BigDecimal(10));
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(sell);
Thread t2 = new Thread(sell);
t1.start();
t1.join();
t2.start();
t2.join();
System.out.println("剩余数量:" + num);
System.out.println("赚到的钱:" + money);
System.out.println("营业员钱:" + reward);
}
}
最后得到的稳定结果 :
剩余数量:0
赚到的钱:5000
营业员钱:500
数据正确了
Synchronized的两个方式
Synchronized的作用,来自orecle官方的一段翻译:
同步方法支持一种简单的策略来防止线程干扰和内存一致性错误: 如果一个对象对多线程可见,则对该对象变量的所有读取或写入都是通过同步方法完成
简句: 能够在同一时间内最多只有一个线程执行某段代码,保证并发的安全性
如果一段代码被Synchronized修饰,那么该段代码会以原子的形式执行,多个线程执行时,不会相互干扰,因为他们不会同时执行
Synchronized被设置成关键字,是Java最基本的互斥同步方式,也是并发编程中必学的一点
1. 对象锁
包括方法锁(默认锁对象为this当前实例对象) 和 同步代码块锁(自定义指定锁对象)
2. 类锁
指synchronized修饰静态的方法或指定锁为Class对象
<1> 测试 对象锁
public class Sell implements Runnable {
static Sell sell = new Sell();
//商品总数
static int num = 500;
//收到的钱 , 每件10块钱
static BigDecimal money = new BigDecimal(0);
//给营业员 , 每件奖励1块钱
static AtomicInteger reward = new AtomicInteger(0);
@Override
public void run() {
synchronized (this) {
for (int i = 0; i < 1000; i++) {
if (num > 0) {
num--;
reward.getAndIncrement();
money = money.add(new BigDecimal(10));
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(sell);
Thread t2 = new Thread(sell);
t1.start();
t2.start();
//保证t1和t2都运行结束,如果没有这段t1和t2没有运行结束就会被打印出去了
while (t1.isAlive() || t2.isAlive()){
}
System.out.println("剩余数量:" + num);
System.out.println("赚到的钱:" + money);
System.out.println("营业员钱:" + reward);
}
}
最后得到的稳定结果 :
剩余数量:0
赚到的钱:5000
营业员钱:500
再复杂一点,比如在一个方法里很多个synchronized块A,B,C,D,执行的方式不是串行的,双双配对方式执行
这时候,我们就需要自己创建对象,充当每一对不同控制的锁对象synchronized(Object){...},这种方式很是理想化,容易出错,也很复杂,而且jdk也提供了更高的线程安全控制api,用他们会更合适一些
分享利用IDEA进行多线程的调试,打上断点,然后点击小红点,有红线划着的All 和 Thread
All : 进入断点时,就会把整个JVM停下来,包括其他的线程,都会一起停下来
Thread: 只会把当前线程停下来
线程的6个状态
- 初始(NEW):新创建一个线程对象,但还没有调用start()方法
- 运行(RUNNABLE):线程中将就绪(ready)和运行中(running)两种状态都称为“运行”,线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready) ,就绪状态的线程在获得CPU时间片后变为运行中状态(running)
- 阻塞(BLOCKED):表示线程阻塞于锁
- 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)
- 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回
- 终止(TERMINATED):表示该线程已经执行完毕
运行起来, 看图 :
虽然2也有状态显示,但是状态的名称不是那么的复合java官方的名词,选择3进入调试,获取更明了的线程生命周期状态
普通方法锁
public class Sell implements Runnable {
static Sell sell = new Sell();
//商品总数
static int num = 500;
//收到的钱 , 每件10块钱
static BigDecimal money = new BigDecimal(0);
//给营业员 , 每件奖励1块钱
static AtomicInteger reward = new AtomicInteger(0);
@Override
public void run() {
selling();
}
public synchronized void selling() {
System.out.println(" 运行开始:" + Thread.currentThread().getName());
for (int i = 0; i < 1000; i++) {
if (num > 0) {
num--;
reward.getAndIncrement();
money = money.add(new BigDecimal(10));
}
}
System.out.println(" 运行结束:" + Thread.currentThread().getName());
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(sell);
Thread t2 = new Thread(sell);
t1.start();
t2.start();
//保证t1和t2都运行结束,如果没有这段t1和t2没有运行结束就会被打印出去了
while (t1.isAlive() || t2.isAlive()) {
}
System.out.println("剩余数量:" + num);
System.out.println("赚到的钱:" + money);
System.out.println("营业员钱:" + reward);
}
}
运动结果 和之前一样:
运行开始:Thread-0
运行结束:Thread-0
运行开始:Thread-1
运行结束:Thread-1
剩余数量:0
赚到的钱:5000
营业员钱:500
下面继续测试,学习....
转载于:https://my.oschina.net/u/3829444/blog/3026233
上一篇: Java多线程机制1
下一篇: 自学java多线程笔记(一)