网站设计数据库怎么做,公司外贸网站怎么做,合肥能做网站的公司,公司企业网站免费建设如何解决线程安全问题
怎么解决线程的安全问题呢#xff1f;
基本上所有解决线程安全问题的方式都是采用“序列化临界资源访问”的方式#xff0c;即在同一时刻只有一个线程操作临界资源#xff0c;操作完了才能让其他线程进行操作#xff0c;也称作同步互斥访问。
在Ja…如何解决线程安全问题
怎么解决线程的安全问题呢
基本上所有解决线程安全问题的方式都是采用“序列化临界资源访问”的方式即在同一时刻只有一个线程操作临界资源操作完了才能让其他线程进行操作也称作同步互斥访问。
在Java中一般采用synchronized和Lock来实现同步互斥访问。
synchronized关键字
首先我们先来了解一下互斥锁互斥锁就是能达到互斥访问目的的锁。
如果对一个变量加上互斥锁那么在同一时刻该变量只能有一个线程能访问即当一个线程访问临界资源时其他线程只能等待。
在Java中每一个对象都有一个锁标记monitor也被称为监视器当多个线程访问对象时只有获取了对象的锁才能访问。
在我们编写代码的时候可以使用synchronized修饰对象的方法或者代码块当某个线程访问这个对象synchronized方法或者代码块时就获取到了这个对象的锁这个时候其他对象是不能访问的只能等待获取到锁的这个线程执行完该方法或者代码块之后才能执行该对象的方法。
我们来看个示例进一步理解synchronized关键字
public class Example {public static void main(String[] args) {final InsertData insertData new InsertData();new Thread() {public void run() {insertData.insert(Thread.currentThread());};}.start();new Thread() {public void run() {insertData.insert(Thread.currentThread());};}.start();}
}
class InsertData {private ArrayListInteger arrayList new ArrayListInteger();public void insert(Thread thread){for(int i0;i5;i){System.out.println(thread.getName()在插入数据i);arrayList.add(i);}}
}这段代码的执行是随机的每次结果都不一样
Thread-0在插入数据0 Thread-1在插入数据0 Thread-1在插入数据1 Thread-1在插入数据2 Thread-1在插入数据3 Thread-1在插入数据4 Thread-0在插入数据1 Thread-0在插入数据2 Thread-0在插入数据3 Thread-0在插入数据4现在我们加上synchronized关键字来看看执行结果
public synchronized void insert(Thread thread){for(int i0;i5;i){System.out.println(thread.getName()在插入数据i);arrayList.add(i);}
}输出
Thread-0在插入数据0 Thread-0在插入数据1 Thread-0在插入数据2 Thread-0在插入数据3 Thread-0在插入数据4 Thread-1在插入数据0 Thread-1在插入数据1 Thread-1在插入数据2 Thread-1在插入数据3 Thread-1在插入数据4可以发现线程1会等待线程0插入完数据之后再执行说明线程0和线程1是顺序执行的。
从这两个示例中我们可以知道synchronized关键字可以实现方法同步互斥访问。
在使用synchronized关键字的时候有几个问题需要我们注意
在线程调用synchronized的方法时其他synchronized的方法是不能被访问的道理很简单一个对象只有一把锁当一个线程在访问对象的synchronized方法时其他线程可以访问该对象的非synchronized方法因为访问非synchronized不需要获取锁是可以随意访问的如果一个线程A需要访问对象object1的synchronized方法fun1另外一个线程B需要访问对象object2的synchronized方法fun1即使object1和object2是同一类型也不会产生线程安全问题因为他们访问的是不同的对象所以不存在互斥问题。
synchronized代码块
synchronized代码块对于我们优化多线程的代码很有帮助首先我们来看看它长啥样
synchronized(synObject) {}当在某个线程中执行该段代码时该线程会获取到该对象的synObject锁此时其他线程无法访问这段代码块synchronized的值可以是this代表当前对象也可以是对象的属性用对象的属性时表示的是对象属性的锁。
有了synchronized代码块我们可以将上述添加数据的例子修改成如下两种形式
class InsertData {private ArrayListInteger arrayList new ArrayListInteger();public void insert(Thread thread){synchronized (this) {for(int i0;i100;i){System.out.println(thread.getName()在插入数据i);arrayList.add(i);}}}
}上述代码就是synchronized代码块添加锁的两种方式可以发现添加synchronized代码块要比直接在方法上添加synchronized关键字更加灵活。
当我们用sychronized关键字修饰方法时这个方法只能同时让一个线程访问但是有时候很可能只有一部分代码需要同步而这个时候使用sychronized关键字修饰的方法是做不到的但是使用sychronized代码块就可以实现这个功能。
并且如果一个线程执行一个对象的非static synchronized方法另外一个线程需要执行这个对象所属类的static synchronized方法此时不会发生互斥现象因为访问static synchronized方法占用的是类锁而访问非static synchronized方法占用的是对象锁所以不存在互斥现象。
来看一段代码
class InsertData {private ArrayListInteger arrayList new ArrayListInteger();private Object object new Object();public void insert(Thread thread){synchronized (object) {for(int i0;i100;i){System.out.println(thread.getName()在插入数据i);arrayList.add(i);}}}
}执行结果
执行insert 执行insert1 执行insert1完毕 执行insert完毕