让我们先来用一个常见的需求来进行思考:电影院三个窗口共同卖出100张票。
我们很容易想到可以用三个线程来模拟三个窗口解决这个问题
public class test {
public static void main(String[] args) {
MyThread t1=new MyThread("t1");
MyThread t2=new MyThread("t2");
MyThread t3=new MyThread("t3");
t1.start();
t2.start();
t3.start();
}
}
class MyThread extends Thread {
public MyThread() {
}
public MyThread(String name) {
super(name);
}
static int ticket=0;//代表票数,static表示三个窗口共享
@Override
public void run() {
while(true) {
ticket++;
if(ticket>100) {
break;
} else
{
System.out.println(currentThread().getName()+"正在卖第"+ticket+"张票");
}
}
}
}
我们看看结果发现他会有重复的票,甚至有出现超出范围的票,这是因为可能在ticket在自增之后,cpu的执行权随时会被其他线程抢走,而多个线程共享一个ticket变量。
为了避免产生这种线程安全问题,提供了三种解决方案
同步代码块能将共享数据代码锁起来,当在某个线程中执行这段代码块时,该线程会获取我们指定的对象的锁,从而使得其他线程无法同时访问该代码块。
synchronized(锁对象){ 操作共享数据的代码 }
1.锁默认打开,当有一个线程进去,线程自动关闭
2.代码执行完毕,线程出来,锁自动打开
3.锁对象可以是任意的,但要是相同唯一的,一般用当前文件的字节码文件对象。
class MyThread extends Thread { public MyThread() { } public MyThread(String name) { super(name); } static int ticket=0;//代表票数 @Override public void run() { while(true) { synchronized (MyThread.class) { try { Thread.sleep(10); } catch (InterruptedException e) { throw new RuntimeException(e); } ticket++; if (ticket > 100) { break; } else { System.out.println(currentThread().getName() + "正在卖第" + ticket + "张票"); } } } } }
如果想要把一个方法里面的代码全部锁起来,就可以用同步方法
格式:
修饰符 synchronized 返回值 方法名(){};
1.同步方法是锁住方法里面的所有代码
2.锁对象不能自己指定(修饰非静态方法:this? ? 静态:当前类的字节码文件对象)
因此当用于 线程继承Thread类 这种创造线程方法时仍然不能解决安全问题。
class MyRunable implements Runnable{ int ticket=0; @Override public void run() { while(true){ if (extracted()) break; } } private synchronized boolean extracted() { if(ticket==100) return true; else{ ticket++; System.out.println(Thread.currentThread().getName()+"在卖第"+ticket+"张票"); } return false; } } public class test { public static void main(String[] args) { MyRunable mr=new MyRunable(); Thread t1=new Thread(mr); Thread t2=new Thread(mr); Thread t3=new Thread(mr); t1.setName("t1"); t1.setName("t2"); t1.setName("t3"); t1.start(); t2.start(); t3.start(); } }
lock能够进行手动的获得锁和释放锁
1.lock是接口,不能直接实例化,下文采用他的实现类Reentrantlock来实例化
2.创建lock对象要用static关键字修饰(使多个线程公共用一把锁)
3.关锁代码不能跳过,否则程序不能停止,可以使用finally代码实现
public class test { public static void main(String[] args) { MyThread t1=new MyThread("t1"); MyThread t2=new MyThread("t2"); MyThread t3=new MyThread("t3"); t1.start(); t2.start(); t3.start(); } } class MyThread extends Thread { public MyThread() { } public MyThread(String name) { super(name); } static int ticket=0;//代表票数 static Lock lock=new ReentrantLock(); @Override public void run() { try { while(true) { lock.lock(); Thread.sleep(10); ticket++; if (ticket > 100) { break; } else { System.out.println(currentThread().getName() + "正在卖第" + ticket + "张票"); } } catch (InterruptedException e) { throw new RuntimeException(e); }finally{ lock.unlock(); } } } }