什么时候会出现线程安全问题?
1.存在多线程的情况
2.多个线程之间存在共享数据
3.存在多条语句操作共享数据
如何解决多线程安全问题?
注意:线程安全执行效率就低,线程不安全,执行效率高
A:同步代码块
synchronized(对象) {
需要被同步的代码}
以3个售票窗口卖票举例:
public class MyThread implements Runnable{
int ticket=70;
Object obj = new Object(); //此对象被用来当作锁
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
//当被同步的代码执行完毕之后,th1手里拿着的obj这个锁才会被释放,
//th1,th2,th3重新抢占cpu的执行权,谁抢到了继续拿着obj这个锁,执行同步代码块中的内容
synchronized (obj) { //同步代码块,括号内的对象即锁
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"正在出售第"+ticket--+"张票");
}else{
break;
}
}
}
}
}
如果不是用同步代码块的话,上述代码会怎么样呢?
会出现几个窗口同时卖一张票的情况,原因就是当天th1进入while循环后,进入休眠,此时因为没有锁,所以th2也能进来,而此时ticket的值并没有自减。
B:同步方法
public synchronized void sellTicket(){同步代码}
public class MyThread implements Runnable{
int ticket=70;
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
sellTicket();
if(ticket<=0){
break;
}
}
}
//同步方法是将synchronized关键字加到方法上,同步方法的锁是this
private synchronized void sellTicket() {
// TODO Auto-generated method stub
if(ticket>0){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"张票");
}
}
}
C:静态同步方法
类的字节码对象
public static synchronized void sellTicket() {
需要同步的代码
}
public class MyThread implements Runnable{
static int ticket=70;
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
sellTicket();
if(ticket<=0){
break;
}
}
}
//静态同步方法,他的锁是本类的字节码文件对象:类名.class
private static synchronized void sellTicket() {
// TODO Auto-generated method stub
if(ticket>0){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"张票");
}
}
}
匿名内部类的方式使用多线程
public static void main(String[] args) {
// TODO Auto-generated method stub
new Thread(){
public void run(){
for(int i=0;i<50;i++){
System.out.println(i);
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<50;i++){
System.out.println(i);
}
}
}).start();
}
JDK5的Lock锁,我们之前造的所有的锁都没有手动释放锁
static Lock lock = new ReentrantLock();
枷锁:lock.lock();
释放锁:lock.unlock();
可以让我们明确的知道在哪里加锁和释放锁。
public class MyThread implements Runnable{
int ticket=70;
//创建一个锁
Lock lock = new ReentrantLock();
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
//上锁
lock.lock();
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+ticket--+"张票");
//释放锁
lock.unlock();
}else{
lock.unlock();
break;
}
}
}
}