上一节课我们学习了java的多线程,ArrayList线程不安全和Vector线程安全
这节课我门改变一下
不在主线程里面打印出mylist的长度了而是在子线程中打印mylist的长度
代码改为下面
MyIndex里面
MyThread下面
运行结果
出现了线程不安全的情况 我们在代码中使用的是Vector集合为什么会出现线程不安全的情况呢?
为了解决这个问题 我们需要在子线程外部中加入一个代码块 这个代码块为同步代码块
代码如下
加入同步代码块,将list对象锁住 即可以解决线程不安全问题
运行结果
运行多次 结果相同
接下来我们把Vector换成ArrayList试一下
MyIndex文件中
运行结果
接下来我们了解一下代码块
代码块就是用{}括起来的部分组成个代码块 代码块是有块级作用域的 嵌套的代码块子代码块可以访问父代码块里的内容
在我们的MyIndex里面写入如下
可以看见输出内容处a报错了 这就是代码块的作用域 外部访问不到a
但是写成下面这样
就不报错了
运行结果
嵌套代码块子代码块可以访问父代码块里的a
一般我们在方法中不会这样写,因为可读性并不高
那么代码块主要用在哪里呢?
1.在类里面
构造代码块
为了演示我们在Core里创建个test文件
代码如下
这时我们在MyIndex里面使用一下
运行结果
可以看出将构造代码块放在上面 构造代码块先执行了
接下来在我们test文件里加入一个构造代码块
运行结果
接下来在test中将代码块1和2位置互换
运行结果
静态代码块
用static关键字加上大括号包起来的代码块
静态代码块只会执行一次
为什么只会执行一次呢?
下面看一下代码 test文件中
运行结果
可以看到静态代码块先执行了
但是我们要知道 静态代码块 和 构造代码块 构造函数并不在同一阶段执行
接下来我们演示一下 在MyIndex里面多实例化几次test
代码如下
运行结果
静态代码块只会执行一次是因为他只在类的初始化阶段执行 而构造代码块和构造函数在类的实例化阶段执行
还有一个有趣的现象是
演示一下 test中代码定义一个静态变量
这时如果我不在MyIndex里面实例化代码
运行结果如下
什么都没输出 因为我没实例化
其实类装载是有过程的 装载-》验证-》解析-》初始化-》实例化等过程
但是我们在MyIndex里面调用一下name
运行结果
可以看见执行了静态代码块里面的内容 因为我们对变量进行调用了 触发了类装载的过程了
接下来再看下同步代码块
同步代码块 其中Vector里面有sync那个同步方法 为什么还会出现线程不安全 为什么需要在子线程里面将list锁住呢
问题在于什么时候释放锁 是因为Vector代码中锁释放的时间不一样 Vector中锁的是this对象Vector实例化过后的对象
其中Vector里面有一个add方法 这个方法执行完之后 锁就释放了 这时另外一个线程对list操作的时候 就可以得到锁了 所以会出现得到size结果不一致的现象
但是我在MyThread中加入同步代码块时 需要将同步代码块中的内容执行完毕 才释放锁
也就是我们下面的代码
这时add执行完成之后 去计算mylist的长度时锁还没被释放
接下来我们在Core里面建一个MyObj.java文件
接下来修改一下MyThread线程代码
使其支持两个方法 先不使用ArrayList和Vector 先传入MyObj参数
接下来来到MyIndex.java文件
实例化MyObj 传入子线程
依然对MyThread循环500次 看一下运行效果 等一下要修改一下MyObj里面的源码(因为ArrayList里面没法改)
运行结果
出现了线程不安全
接下来在MyObj里面函数部分加入同步关键字synchronized
运行结果
依然还是线程不安全的
那么如何解决呢?
这时来到MyThread类
加入同步代码块 将myObj锁住 一旦锁住之后 代码块里面的内容只能有一个线程执行 下一个线程只有等上一个线程执行完成之后才能执行
运行结果一定是500的倍数
结果如下
但是如果我们不想使用同步代码块怎么办呢?
MyThread里面
MyObj里面
运行结果
这里只能保证运行结果是500的倍数 因为是两个同步代码方法