线程安全问题:

简单来说,就是多个线程在操作同一个变量时引起的问题。

这里是用一个简单的例子说明一下:

以Runnable创建的线程为例:一个售票系统,count代表当前票数,卖出一张count--。

Runnable线程类:

public class Runnable_Exp implements Runnable{
private int count = 50;
@Override
public void run() {
while(true)
if(count >= 0){
System.out.println(Thread.currentThread().getName() +"现在的数:"+count);
count--;
}else break;
}
}

在main调用数个相同的Runnable线程:

Runnable_Exp rExp = new Runnable_Exp();
Thread trs1 = new Thread(rExp,"线程1");
Thread trs2 = new Thread(rExp,"线程2");
Thread trs3 = new Thread(rExp,"线程3");
Thread trs4 = new Thread(rExp,"线程4");
trs1.start();trs2.start();trs3.start();trs4.start();

运行:

我们会发现一个问题,票数会有相同的!

这就是线程安全的体现。此时我们就需要使用线程锁解决这个问题。

另外,我们之前学习的集合类中,有分线程安全与线程不安全的两类。

StringBuffer和Vector是线程安全的。

StringBuilder和ArrayList是线程不安全的。

在多线程中,我们不可以使用多个线程同时操作不安全的集合类


线程锁的使用:synchronized

Runnable中使用线程锁:

我们只需要修改一下Runnable线程类。

public class Runnable_Exp implements Runnable{
private int count = 500; private Object lock = new Object(); @Override
public void run() {
while(true)
synchronized (lock) {
if (count >= 0) {
System.out.println(Thread.currentThread().getName() + "现在的数:" + count);
count--;
} else break;
}
}
}

解析:

添加一个线程锁对象,一般使用Object即可。

private Object lock = new Object();

这个Object对象lock是四个线程共享的(且这个lock一定是共用的!)可以自己尝试一下把锁放进run()中。

synchronized (lock)

当线程一运行到上面这一行的时候,线程一就会占用lock对象(相当于上锁了),直到线程一执行完synchronized语句块内的全部代码,lock对象就会取消占用。

若线程二运行到synchronized语句时,发现lock对象已经被占用了,则会等待,直到lock被取消占用。

有可能多个线程一起抢占。

效率会比较低。毕竟是你执行完再到我执行。无法同步执行。这就是所谓“抢占式”执行。

Thread中使用线程锁:

Thread线程类中:

public class Thread_Exp extends Thread {
public Thread_Exp(){}
public Thread_Exp(String name){
super(name);
}
private static Object lock = new Object();
private static int count = 100;
public void run(){
while(true)
synchronized(lock){
if (count >= 0) {
System.out.println(Thread.currentThread().getName() + "现在的数:" + count);
count--;
} else break;
}
}
}

需要注意的是,这里的线程锁对象和count都需要设置为静态的!

Thread_Exp t1 = new Thread_Exp("线程1");
Thread_Exp t2 = new Thread_Exp("线程2");
Thread_Exp t3 = new Thread_Exp("线程3");
Thread_Exp t4 = new Thread_Exp("线程4");
t1.start();t2.start();t3.start();t4.start();

不创建Object对象可以实现相同的操作吗?

答案是可以的,我们可以不用创建Object类型的lock对象。

我们直接用synchronized(this)就可以了。

相当于检测当前对象是非被线程占用了(加锁)。

实例:

@Override
public void run() {
synchronized (this){
while(true)
if (count >= 0) {
System.out.println(Thread.currentThread().getName() + "现在的数:" + count);
count--;
} else break;
}
}

我们还可以直接使用synchronized关键字描述一个方法,具体请看下文。


使用synchronized方法简化上述代码:

同步方法,顾名思义,就是自带线程锁的方法。

只需要在方法的返回值前面添加synchronized关键字即可。

原先的代码我们可以写成:

使得run()方法更加简洁。

@Override
public void run() {
Count();
} public synchronized void Count(){
while(true)
if (count >= 0) {
System.out.println(Thread.currentThread().getName() + "现在的数:" + count);
count--;
} else break;
}

使用同步方法,这个方法体就会自动被加锁。


关于线程安全的方法:

上面我们说了StringBuffer方法是线程安全的,那我们是怎么知道的呢?

我们可以直接查看StringBuffer中方法的源代码。

例如attend方法:

@Override
@IntrinsicCandidate
public synchronized StringBuffer append(char c) {
toStringCache = null;
super.append(c);
return this;
}

我们可以看到这个方法是synchronized方法。

大家可以自行去研究一下各种Java自带的方法,是否是线程安全的。

最新文章

  1. php缓冲区详解
  2. 探索c#之递归APS和CPS
  3. [转]C#网络编程(异步传输字符串) - Part.3
  4. Query classification; understanding user intent
  5. Java [Leetcode 228]Summary Ranges
  6. FastCGI技术
  7. oracle级联删除 触发器
  8. java中的string字符串中的trim函数的作用
  9. Android中的RelativeLayout
  10. win7 奇怪的temp用户
  11. 怎样给你的Android 安装文件(APK)减肥
  12. TensorFlow之RNN:堆叠RNN、LSTM、GRU及双向LSTM
  13. 区分range() , np.arange() , np.linspace()
  14. Aircrack-ng破解WPA/WPA2加密WiFi教程(Kali)
  15. IOS Block代码块的定义与使用
  16. android 之TCP客户端编程
  17. 团队作业四-WBS练习
  18. Beta冲刺——day3
  19. 监控cpu、内存 <shell>
  20. JaVA web服务器配置

热门文章

  1. linux-python安装pip
  2. Operator介绍
  3. 『现学现忘』Git后悔药 — 27、版本回退介绍
  4. 选择结构-单if语句和标准if else语句
  5. 聊聊 C++ 中几类特殊成员函数
  6. Linux 更改家目录下的目录为英文
  7. ApiDay001 __02 Java_StringBuilder
  8. 跟我读论文丨Multi-Model Text Recognition Network
  9. java的访问权限protected和default
  10. js入门基础