同步机制解决线程安全问题(2020-03-31)
2020-03-31 本文已影响0人
_NewMoon
如何解决线程之前提到的线程安全问题?
Java提供了一种同步机制来解决这种问题:
1.同步代码块
我们先介绍synchronized关键字,synchronized关键字是用来给对象和方法或者代码块加锁,同一时刻只有一个线程可以执行加锁的这段代码,其他进程都会被阻塞,等待这段代码执行后,其他线程才能执行这段代码。
我们对线程安全问题引入这篇博客中的Ticket.java进行修改:
Ticket.java
package Thread.Problem;
public class Ticket implements Runnable
{
//总票数
private int tik = 100;
Object obj = new Object();
@Override
public void run() {
while(true) {
synchronized (obj){
if(tik>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"is sailing No." + tik);
tik--;
}
}
}
}
}
程序运行结果

我们发现,没有出现之前提到的安全问题,但是从运行结果来看,可以发现所有的票都是从1号窗口(线程0)售卖出去的,那么其他两个线程是不是没有作用,其实,在这个例子中,由于三个线程访问的都是tik这一个数据,所以synchronized锁住的部分在本例中就是售票的那一部分,而在实际情况中,往往会复杂很多,synchronized也只会锁住具体的同步位置,所以,这个例子不能说明synchronized对破坏了多线程特性,但需要承认的是,在这里,同步代码块会降低运行效率,当一个线程执行同步代码块中的代码,其他线程只能死等,等待锁对象的归还。
2.同步方法
顾名思义,就是将方法用synchronized关键字修饰,所以,只需将需要同步的代码抽取出来,重新写一个同步方法,再在run方法中原来被抽取代码的地方调用同步方法即可。
Ticket.java
package Thread.Problem;
public class Ticket implements Runnable
{
//总票数
private int tik = 100;
Object obj = new Object();
@Override
public void run() {
while(true) {
sailTicket();
}
}
public synchronized void sailTicket(){
if(tik>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"is sailing No." + tik);
tik--;
}
}
}
输出与上一种方法相同,指出一点,同步方法中sailTicket方法中synchronized锁的对象是this(即类的实例),所以这里的同步方法也可以这样更改:
public void sailTicket(){
synchronized (this){
if(tik>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"is sailing No." + tik);
tik--;
}
}
}
静态同步方法
我们也可以将同步方法设置为静态方法,不过此时synchronized锁的对象就不是this对象锁了,而是类锁,本例中,就是Ticket.class
package Thread.Problem;
public class Ticket implements Runnable
{
//总票数
private static int tik = 100;
Object obj = new Object();
@Override
public void run() {
while(true) {
sailTicket();
}
}
public static void sailTicket(){
synchronized (Ticket.class){
if(tik>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"is sailing No." + tik);
tik--;
}
}
}
}