Java的多线程编程中,线程安全是一个关键概念。线程安全指的是多个线程同时访问共享数据时,不会导致数据损坏或不一致的状态。为了实现线程安全,可以使用同步机制,如synchronized关键字或Lock接口,来保护共享资源的访问。此外,Java提供了线程安全的集合类,如ConcurrentHashMap和CopyOnWriteArrayList,用于处理多线程环境下的数据共享。正确的线程安全实践可以确保程序在多线程环境下稳定可靠地运行,避免竞态条件和数据冲突问题。
🌊 关注我不迷路,如果本篇文章对你有所帮助,或者你有什么疑问,欢迎在评论区留言,我一般看到都会回复的。大家点赞支持一下哟~ 💗
为什么会出现线程安全问题?
线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
示例:
class TicketRunnable implements Runnable{
private int ticket=100;
//每个窗口卖票的操作
//窗口 永远开启
@Override
public void run() {
while(true){//有票可以卖
//出票操作
if(ticket>0){
//使用sleep模拟一下出票时间 //模拟一下出票的时间
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在卖票:"+ticket--);
}
}
}
}
public class ThreadSafe {
public static void main(String[] args) throws Exception{
TicketRunnable t = new TicketRunnable();
Thread t1 = new Thread(t,"窗口1");
Thread t2 = new Thread(t,"窗口2");
Thread t3 = new Thread(t,"窗口3");
//3个窗口同时卖票
t1.start();
t2.start();
t3.start();
}
}为了保证每个线程都能正常执行原子操作,Java引入了线程同步机制。那么怎么去使用呢?有三种方式完成同步操作:
语法:
synchronized(临界资源对象){ //对临界资源对象加锁
//代码(原子操作)
}
同步锁:
对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.
注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着(BLOCKED)。
示例:
class Ticket2 implements Runnable{
private int ticket=100;
Object lock = new Object();
//每个窗口卖票的操作
//窗口 永远开启
@Override
public void run() {
while(true){//有票可以卖
synchronized(lock){//synchronized (this) {//this ---当前对象
if(ticket>0){
//出票操作
//使用sleep模拟一下出票时间
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在卖票:"+ticket--);
}
}
}
}
}
public class TicketDemo2 {
public static void main(String[] args) {
Ticket2 ticket2 = new Ticket2();
Thread t1 = new Thread(ticket2,"窗口1");
Thread t2 = new Thread(ticket2,"窗口2");
Thread t3 = new Thread(ticket2,"窗口3");
//3个窗口同时卖票
t1.start();
t2.start();
t3.start();
}
}同步方法 :使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。
语法:
synchronized 返回值类型 方法名称(形参列表){ //对当前对象(this)加锁
// 代码(原子操作)
}
示例:
class Ticket3 implements Runnable{
private int ticket=100;
//Object lock = new Object();
//每个窗口卖票的操作
//窗口 永远开启
@Override
public void run() {
while(true){//有票可以卖
sellTicket();
if(ticket<=0){
break;
}
}
}
/**
* 锁对象,谁调用这个方法,就是谁
* 隐含锁对象,就是this
*
* 静态方法,隐含锁对象就是Ticket3.class
*/
public synchronized void sellTicket(){
if(ticket>0){
//出票操作
//使用sleep模拟一下出票时间
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在卖票:"+ticket--);
}
}
}
public class TicketDemo3 {
public static void main(String[] args) {
Ticket3 ticket3 = new Ticket3();
Thread t1 = new Thread(ticket3,"窗口1");
Thread t2 = new Thread(ticket3,"窗口2");
Thread t3 = new Thread(ticket3,"窗口3");
//3个窗口同时卖票
t1.start();
t2.start();
t3.start();
}
}synchronized注意点
同步锁是谁?
对于非static方法,同步锁就是this。
对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。
常用方法:
方法名 | 描述 |
|---|---|
void lock() | 获取锁,如锁被占用,则等待。 |
boolean tryLock() | 尝试获取锁(成功返回true。失败返回false,不阻塞)。 |
void unlock() | 释放锁。 |
ReentrantLock:
示例:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyList {
//创建锁
private Lock lock = new ReentrantLock();
private String[] str = {"A","B","","",""};
private int count = 2;
public void add(String value){
//当没有锁的时候,会出现覆盖的情况
str[count] = value;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
System.out.println(Thread.currentThread().getName()+"添加了"+value);
// lock.lock();
// try {
// str[count] = value;
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// count++;
// System.out.println(Thread.currentThread().getName()+"添加了"+value);
// }finally {
// lock.unlock();
// }
}
public String[] getStr(){
return str;
}
}测试:
public class TestMyList {
public static void main(String[] args) throws InterruptedException {
MyList myList = new MyList();
//
Thread t1 =new Thread(new Runnable() {
@Override
public void run() {
myList.add("hello");
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
myList.add("world");
}
});
t2.start();
t1.join();
t2.join();
String[] str = myList.getStr();
for (String s : str) {
System.out.println("s:"+s);
}
}
}
本期结束咱们下次再见👋~
🌊 关注我不迷路,如果本篇文章对你有所帮助,或者你有什么疑问,欢迎在评论区留言,我一般看到都会回复的。大家点赞支持一下哟~ 💗

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。