卖演唱会票例子
保证:多个线程访问共享的数据(数据只有一份)
public static void main(String[] args) {
//创建3个线程
TicketRunnable runnable=new TicketRunnable();
Thread t1=new Thread(runnable);
Thread t2=new Thread(runnable);
Thread t3=new Thread(runnable);
//开启线程
t1.start();
t2.start();
t3.start();
}
//接口实现线程的方式
class TicketRunnable implements Runnable{
//声明票
private int ticket=50;
//声明锁对象
private Object object=new Object();
@Override
public void run() {
//循环卖
while (true) {
//同步锁
synchronized (object) {
//判断卖完没
if (ticket>0) {
//为了测试结果更明显加个线程休眠
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//卖
System.out.println(Thread.currentThread().getName()+"*****"+ticket);
//卖一张 少一张
ticket--;
}else {
//卖光了 结束循环
break;
}
}
//让出cpu的执行资源(随机让出)
Thread.yield();
}
}
}
同步代码块规则:
当线程进入同步代码块的时候
先看一下有没有锁
如果有锁 就进入同步代码块中执行代码
进去的同时会获取这把锁
当代码执行完毕 出同步代码块时 将这把锁释放(还回去)
如果没锁 线程在同步代码块前等待(等着有锁才能进)
同步代码块(同步锁)
锁:任意对象(只有一把锁 使用的是同一把锁)
synchronized(锁){
操作共享数据的代码
}
同步锁
好处:数据安全
坏处:效率会降低(获取锁和释放锁 会耗费资源)
同步方法(作用和同步代码块相同)
也是用synchronized关键字
该关键字声明在方法上
同一时间 只能有一个线程进入到同步方法中执行代码
同步方法中 也是写操作共享数据的代码
处理方式和同步代码块一样
public synchronized void sellTicket() {
}
JDK1.5 Lock 接口
使用实现类 ReentrantLock
lock();获取锁
unlock();释放锁(保证锁一定会被释放)
使用前提:和同步代码块一样 要保证用的是同一把锁
使用格式:
try{
操作共享数据的代码
}finally{
释放锁
}
while (true) {
//获取锁
lock.lock();
try {
} finally {
lock.unlock();
}
}
线程死锁
前提:必须要有同步锁的嵌套
2.锁对象要唯一(使用的是同一把锁)
两把锁都要保证唯一
synchronized(){
synchronized(){
}
}
public static void main(String[] args) {
//测试死锁
DieLockRunnable runnable=new DieLockRunnable();
Thread t1=new Thread(runnable);
Thread t2=new Thread(runnable);
t1.start();
t2.start();
}
//创建A锁
class LockA{
//为了唯一 不让外人创建 自己创建
private LockA(){
}
//定义一个常量
public static final LockA LOCK_A=new LockA();
}
//创建B锁
class LockB{
//为了唯一 不让外人创建 自己创建
private LockB(){
}
//定义一个常量
public static final LockB LOCK_B=new LockB();
}
class DieLockRunnable implements Runnable{
//声明一个标记
private boolean isFlag=true;
//第一次 先进A锁再进B锁
//下一次 先进B锁再进A锁
@Override
public void run() {
//死循环 为了增加死锁的几率
while (true) {
if (isFlag) {
//A->B
synchronized (LockA.LOCK_A) {
System.out.println("我是if的 LOCK_A锁");
synchronized (LockB.LOCK_B) {
System.out.println("我是if的 LOCK_B锁");
}
}
}else {
//B->A
synchronized (LockB.LOCK_B) {
System.out.println("我是else的LOCK_B锁");
synchronized (LockA.LOCK_A) {
System.out.println("我是else的 LOCK_A锁");
}
}
}
//修改标记
isFlag=!isFlag;
}
}
}
线程如何停止?
interrupt()方法 中断线程
1.调用interrupt方法是 线程中有wait()/sleep()等方法 这时会抛出一个异常
interruptException异常 并且清除中断状态
2.调用interrupt方法时 线程中没有上述方法
这时会设置(改变)中断状态的值(true---false)
public static void main(String[] args) {
InterruptRunnable runnable=new InterruptRunnable();
Thread t1=new Thread(runnable);
t1.start();
//给子线程几秒的执行时间 然后再中断线程
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//中断线程
t1.interrupt();
//标记中断线程
runnable.isFlag=true;
System.out.println("中断线程");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("主线程结束");
}
class InterruptRunnable implements Runnable{
//声明一个标识 用来停止线程
public boolean isFlag=false;
@Override
public void run() {
//直接使用中断状态来中断线程
while (!Thread.interrupted()) {
//休眠
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//休眠一秒(循环卡1秒)
//long time = System.currentTimeMillis();
//while (System.currentTimeMillis()-time<1000) {
//}
System.out.println(Thread.currentThread().getName()+"...run");
}
}
}
调用interrupt方法时
线程中有wait()方法
wait()方法 线程等待并且如果没有被唤醒 就一直等待下去
是Object类中的方法
直接使用wait()方法会出现异常
注意:wait()方法必须使用锁对象去调用
public static void main(String[] args) {
//创建两个线程 测试
WaitRunnable runnable=new WaitRunnable();
Thread t1=new Thread(runnable);
Thread t2=new Thread(runnable);
t1.start();
t2.start();
for (int i = 0; i < 50; i++) {
if (i==25) {
//调用interrupt方法测试
t1.interrupt();
t2.interrupt();
//更改标记来中断线程
runnable.isFlag=true;
}
System.out.println(i);
}
System.out.println("主线程结束");
}
class WaitRunnable implements Runnable{
//标记中断线程
public boolean isFlag=false;
@Override
public synchronized void run() {
while (!isFlag) {
try {
//线程等待
//InterruptedException异常 抛出了
//线程从等待状态变成运行状态
//解除了等待状态
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"...run");
}
}
}
从子线程中更改中断线程的标记
让主线程能够接到这个标记改变
public static void main(String[] args) {
VolRunnable runnable=new VolRunnable();
Thread t1=new Thread(runnable);
t1.start();
//利用中断状态卡住主线程
while (!runnable.isFlag) {
}
System.out.println("主线程结束");
}
class VolRunnable implements Runnable{
//当子线程中修改状态 不能及时同步到主线程中
//这时可以使用volatile关键词
//让线程中的这个状态信息 可以及时得到修改
//标记
public volatile boolean isFlag=false;
//声明一个变量 记录什么时候更改状态
private int num=0;
@Override
public void run() {
while (!isFlag) {
num++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (num>5) {
//更改中断状态
isFlag=true;
}
System.out.println(Thread.currentThread().getName()+"...run");
}
}
}