synchronized修饰方法
在前面的博客中我向大家展示了线程不安全的案例以及可能造成的危害,今天给大家讲解防止危险的方法。
我们可以使用synchronized关键字来修饰变量或方法
修饰方法之后,方法体中的this指针所指对象会被标记为同步对象。此时其他对象访问该对象会进入阻塞状态,只有对象被访问结束才会释放资源。
显而易见的是这种行为无疑会消耗更多的资源,并降低程序的执行效率。
package com.UnSafeDemo;
public class sellTickets extends Thread{
@Override
public synchronized void run() {
sell();
}
private int tickets = 3;
public static void main(String[] args) {
sellTickets machine = new sellTickets();
new Thread(machine,"张三").start();
new Thread(machine,"李四").start();
new Thread(machine,"黄牛党").start();
}
void sell()
{
while (this.tickets>0)
{
System.out.println(Thread.currentThread().getName()+"买走了第"+tickets+"张票");
tickets--;
}
}
}
以上便是改良过的售票案例。
观察结果我们发现,确实解决掉了同一张票被多个人买到的问题。但是由于synchronized用来修饰run方法。导致张三在买票的时候全部买完,其他人才可以买,这种霸道的做法显然是我们不想看到的。
package com.UnSafeDemo;
public class sellTickets extends Thread {
boolean flag = true;
@Override
public void run() {
while (flag) {
sell();
}
}
private int tickets = 100000;
public static void main(String[] args) {
sellTickets machine = new sellTickets();
new Thread(machine, "李四").start();
new Thread(machine, "张三").start();
new Thread(machine, "黄牛党").start();
}
synchronized void sell() {
if (tickets <= 0) {
flag = false;
return;
} else {
System.out.println(Thread.currentThread().getName() + "买走了第" + tickets + "张票");
tickets--;
}
}
}
这次我们把synchronized用来修饰sell方法,并把循环写到run方法之中。
这样程序无需等待每一张票全部卖完再释放锁,从而解决掉上述问题。
synchronized(obj){}同步代码块
这里的obj即为我们需要上锁的变化的且多个线程共享的变量,而{}中的代码为操作obj的代码段。
注意理论上来讲我们可以将一切资源及代码块上锁。但这样会大大降低性能。保证安全性的同时提高性能便是我们该做的事。
package com.UnSafeDemo;
public class sellTickets extends Thread {
boolean flag = true;
@Override
public void run() {
synchronized (tickets)
{
while (flag) {
sell();
}
}
}
private Integer tickets = 10;
public static void main(String[] args) {
sellTickets machine = new sellTickets();
new Thread(machine, "李四").start();
new Thread(machine, "张三").start();
new Thread(machine, "黄牛党").start();
}
void sell() {
if (tickets <= 0) {
flag = false;
return;
} else {
System.out.println(Thread.currentThread().getName() + "买走了第" + tickets + "张票");
tickets--;
}
}
}
这里一定要注意!被synchronized中的被上锁的变量一定是object类型或者其子类,如果使用基础数据类型如int float 会报错,应使用其相应的包装类如Interger
由于在run方法中的sell方法多个线程均可访问到变量tickets ,我们只需要
synchronized(tickets){sell()}即可实现。