实现线程有两种方法,一种继承 Thread 类,一种实现接口 Runnable ,但是这两种方法都存在线程不安全的问题
目录
线程安全问题引入
当某个线程操作的过程中,尚未操作完成时,其他线程参与进来,也进行同样的操作,就会引发线程不安全的问题
我们先看一个 Thread 的例子,一共有 100 张票,我们通过三个窗口卖出
class myTest extends Thread{
private int ticket = 100;
@Override
public void run() {
while(true){
if (ticket > 0){
System.out.println(Thread.currentThread().getName() + "号码是:" + ticket);
ticket--;
}else {
break;
}
}
}
}
public class threadTest {
public static void main(String[] args) {
myTest t = new myTest();
myTest t1 = new myTest();
myTest t2 = new myTest();
t.start();
t1.start();
t2.start();
}
}
这个例子中就存在多线程的安全问题,在运行的时候会出现多个窗口的票是重复的情况,这在现实中是不存在的
怎么解决这个问题呢?
解决线程安全问题的方案
当一个线程 a 在操作的时候,其他线程不能参与进来。直到线程 a 操作完成,其他线程才可以操作。这种情况即使线程 a 出现了阻塞,也不能改变
在 Java 中,通过同步机制,来解决线程的安全问题
我们使用同步代码块或者同步方法来完成上述操作,解决线程安全问题
注:操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程过程,效率较低
同步代码块
代码格式:
synchronized (同步监视器) {
// 需要被同步的代码
}
解决接口 Runnable 的线程安全问题
1. 操作共享数据的代码,即为需要被同步的代码
2. 共享数据:多个线程共同操作的变量
3. 同步监视器:就是 “锁” 。任何一个类的对象都可以充当锁
注:多个线程必须要共用同一把锁
4. 在实现接口 Runnable 中,同步监视器我们可以使用 this ,此时的 this 代表唯一的当前类对象
class Test1 implements Runnable{
private int ticket = 100;
Object obj = new Object();
@Override
public void run() {
while(true){
synchronized(obj) {// 此处的 obj 我们使用 this 也可以,此时的 this 代表唯一的 Test1 对象
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "号码是:" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
public class RunnableTest {
public static void main(String[] args) {
Test1 test1 = new Test1();
Thread t = new Thread(test1);
Thread t1 = new Thread(test1);
t.start();
t1.start();
}
}
解决继承 Thread 类的线程安全问题
1. 在继承 Thread 类创建多线程的方式中,慎用 this 充当同步监视器,考虑使用当前类充当同步监视器
2. 创建同步监视器记得声明 static,让多个对象共用一个锁
class myTest extends Thread{
private static int ticket = 100;
private static Object obj = new Object(); // 注意声明 static,让多个对象共用一个锁
@Override
public void run() {
while(true){
synchronized(obj) {// 不能使用 this 是因为此时的 this 代表着多个类对象
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "号码是:" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
public class threadTest {
public static void main(String[] args) {
myTest t = new myTest();
myTest t1 = new myTest();
myTest t2 = new myTest();
t.start();
t1.start();
t2.start();
}
}
同步方法
代码格式:
使用权限 synchronized void xxx () {
}
1. 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明
2. 非静态的同步方法,同步监视器是:this
静态的同步方法,同步监视器是:当前类本身
解决接口 Runnable 的线程安全问题
class Test1 implements Runnable{
private int ticket = 100;
@Override
public void run() {
while(true){
show();
}
}
private synchronized void show(){// 同步监视器:this
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "号码是:" + ticket);
ticket--;
}
}
}
public class RunnableTest {
public static void main(String[] args) {
Test1 test1 = new Test1();
Thread t = new Thread(test1);
Thread t1 = new Thread(test1);
t.start();
t1.start();
}
}
解决继承 Thread 类的线程安全问题
class myTest extends Thread{
private static int ticket = 100;
@Override
public void run() {
while(true){
show();
}
}
private static synchronized void show(){// 同步监视器:myTest.class
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "号码是:" + ticket);
ticket--;
}
}
}
public class threadTest {
public static void main(String[] args) {
myTest t = new myTest();
myTest t1 = new myTest();
myTest t2 = new myTest();
t.start();
t1.start();
t2.start();
}
}
解决线程安全问题的其他方法:
Lock锁解决线程安全问题:https://blog.csdn.net/lijibai_/article/details/123828744