1.1、synchronized
同步代码块
synchronized(obj)
{
操作共享数据的代码;
}
表示线程开始执行同步代码块之前,必须先获取对同步监视器的锁定,obj就是同步监视器。
注意:
1、任何时刻只能有一个线程可以获取对同步监视器的锁定,当同步代码块执行结束后,该线程自动释放对同步监视器的锁定。
2、synchronized可以用于修饰方法,也可以用于修饰代码块,但是不能修饰构造方法以及属性,如有用于修饰方法,同步监视器就是当前类的对象,同步代码块中就可以使用this,同步代码块中的监视器不一定是当前类的对象。
3、如果一个同步代码块和非同步代码块同时操作共享资源,仍然会造成共享资源的竞争。
4、每个对象都有唯一的同步锁。
5、在静态方法前面也可以使用synchronized修饰符。
6、synchronized修饰符不会被继承,不影响重写(不受重写的约束)。
临界资源:
通过synchronized修饰的代码就是可以保证并发线程在任意时刻只有一个线程可以进入修改共享资源的代码区,该区也被称为临界资源(临界区)
使用同步方法的特征:
1、该类的对象可以被多线程安全的访问。
2、每个线程调用该对象的任意方法后都将得到正确的结果。
3、每个线程调用对象的任意方法,该对象的状态依然保持合理的状态。
什么时候会释放锁(同步监视器):
1、同步代码块或同步方法执行完成,自动释放锁。
2、在代码块中遇到break/return跳出该代码块/方法,自动释放锁。
3、执行不同代码块或同步方法时,遇到未处理的异常。
4、调用了同步监视器的wait()方法,但是sleep/yield不会释放锁。
package com.wlx.day11;
/**
* 使用多线程完成飞机购票,某订票系统还剩5张票,现有3个人去买票,
* 在买票时,会出现系统延迟的情况,请使用程序实现3人购票的情况。
*/
public class Ticket implements Runnable
{
//定义一个变量用于缓存剩余的5张票
private int p = 5;
@Override
public void run()
{
for (int i = 0; i < 10; i++)
{
buyTicket();
}
}
/**
* 被synchronized修饰的方法就是同步方法
*/
private synchronized void buyTicket() {
if(p > 0)
{
try {
System.out.println("系统延迟中...");
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"---买票"+(p--));
}
}
public static void main(String[] args)
{
Ticket t = new Ticket();
//创建3个线程代表3个人
Thread tom = new Thread(t,"tom");
Thread jim = new Thread(t,"jim");
Thread lilei = new Thread(t,"lilei");
//3个人去买票
tom.start();
jim.start();
lilei.start();
}
}
1.2、Lock
package com.wlx.day11;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 使用多线程完成飞机购票,某订票系统还剩5张票,现有3个人去买票,
* 在买票时,会出现系统延迟的情况,请使用程序实现3人购票的情况。
*/
public class LockTicket implements Runnable
{
//定义一个变量用于缓存剩余的5张票
private int p = 5;
//创建一个锁对象
Lock lock = new ReentrantLock();
@Override
public void run()
{
for (int i = 0; i < 10; i++)
{
buyTicket();
}
}
/**
* 使用Lock为临界资源加锁
*/
private void buyTicket() {
//加锁
lock.lock();
if(p > 0)
{
try {
System.out.println("系统延迟中...");
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"---买票"+(p--));
}
//释放锁
lock.unlock();
}
public static void main(String[] args)
{
LockTicket t = new LockTicket();
//创建3个线程代表3个人
Thread tom = new Thread(t,"tom");
Thread jim = new Thread(t,"jim");
Thread lilei = new Thread(t,"lilei");
//3个人去买票
tom.start();
jim.start();
lilei.start();
}
}
1.3、死锁
一个线程的锁中在没有释放锁之前,包含了其他的没有释放的锁,此时就会有可能产生死锁,多线程编程时应该避免死锁。
package com.wlx.day11;
public class TangBoHu
{
public void say()
{
System.out.println("你先将秘籍给我,我就将画给你...");
}
public void getMiji()
{
System.out.println("唐伯虎得到武功秘籍...");
}
}
package com.wlx.day11;
public class DongFangbb
{
public void say()
{
System.out.println("你先将画给我,我就将秘籍给你...");
}
public void getHua()
{
System.out.println("东方不败得到百鸟朝凤图...");
}
}
package com.wlx.day11;
public class MijiHua implements Runnable
{
private static TangBoHu tangBoHu = new TangBoHu();
private static DongFangbb dongFangbb = new DongFangbb();
boolean flag = false;
@Override
public void run()
{
if(flag)
{
synchronized (dongFangbb) {
try {
dongFangbb.say();
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (tangBoHu) {
tangBoHu.getMiji();
}
}
}
else
{
synchronized (tangBoHu) {
try {
tangBoHu.say();
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (dongFangbb) {
dongFangbb.getHua();
}
}
}
}
public static void main(String[] args) {
MijiHua mijiHua1 = new MijiHua();
MijiHua mijiHua2 = new MijiHua();
mijiHua1.flag = true;
// mijiHua2.flag = false;
Thread t = new Thread(mijiHua1,"tbh");
Thread d = new Thread(mijiHua2,"dfbb");
t.start();
d.start();
}
}
店铺
package com.wlx.day11;
/**
* 华为手机店铺
*/
public class MobileTelShop
{
//声明一个手机数量缓存变量
private int num;
//通过标识位完成先提供产品还是先去消费
private boolean flag = false;
/**
* 提供者提供产品的方法
*/
public synchronized void producer(int num)
{
/*
如果flag为true,表示手机产品还有货,提供者则等待消费者消费完成以后再
继续提供产品
*/
if(flag)
{
try {
wait();//等待消费者消费产品
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else
{
//flag为false时,表示提供者需要继续提供产品
this.num = num;
flag = true;
System.out.println("提供"+num+"个产品");
//通知消费者消除去消费,即唤醒消费者线程
notify();
}
}
/**
* 消费者消费产品的方法
*/
public synchronized void consumer()
{
/*
如果flag为false,表示手机产品已经消费完,消费者等待提供者提供产品以后
在继续消费
*/
if(flag)
{
flag = false;
System.out.println("消费了"+num+"个产品");
//通知提供继续提供产品,即唤醒提供者线程
notify();
}
else
{
try {
wait();//等待提供者提供产品
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
生产者
package com.wlx.day11;
/**
* 提供者线程
*/
public class Producer implements Runnable
{
//店铺对象
private MobileTelShop mobileTelShop;
public Producer(MobileTelShop mobileTelShop)
{
this.mobileTelShop = mobileTelShop;
}
/**
* 提供产品的线程
*/
@Override
public void run()
{
try {
for (int i = 0; i < 10; i++) {
mobileTelShop.producer((int)(Math.random()*100));
Thread.sleep(500);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
消费者
package com.wlx.day11;
/**
* 消费者线程
*/
public class Consumer implements Runnable
{
private MobileTelShop mobileTelShop;
public Consumer(MobileTelShop mobileTelShop)
{
this.mobileTelShop = mobileTelShop;
}
/**
* 消费产品的方法
*/
@Override
public void run()
{
try {
for (int i = 0; i < 10; i++)
{
mobileTelShop.consumer();
Thread.sleep(500);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试类
package com.wlx.day11;
public class MobileTelShopTesting
{
public static void main(String[] args) {
MobileTelShop mobileTelShop = new MobileTelShop();
Thread t = new Thread(new Consumer(mobileTelShop),"消费者");
Thread d = new Thread(new Producer(mobileTelShop),"提供者");
t.start();
d.start();
}
}