同步方法
package com.thread.ticket3;
/**
* 同步代码块中的对象针对多个线程必须是同一个,
* 其实这个对象被称为同步锁对象
*
* 同步代码块的锁对象是谁呢?
* 任意对象
*
* 如果一个方法,一进去我们就看到代码被同步了,所以,我就想,这个同步能不能加在方法上面呢?
* 能:这个东西被称为同步方法:就是把同步关键字加在同步方法上。
*
* 现在我们在这样的一个代码中
* if(){
* 同步代码块
* }else{
* 同步方法
* private synchronized void show()
* }
*
* 又会出现负票,为什么会有问题呢?
* 就是同步的代码,必须保证锁对象一致。
* 同步方法的锁对象是谁呢?
* this对象
* 静态同步方法的锁对象是谁呢?
* Ticket.class 字节码文件对象
* 当前类的字节码文件对象
*/
public class TicketDemo {
public static void main(String[] args) {
Ticket t1 = new Ticket();
Thread t = new Thread(t1, "窗口1");
Thread t4 = new Thread(t1, "窗口2");
Thread t2 = new Thread(t1, "窗口3");
Thread t3 = new Thread(t1, "窗口4");
t.start();
t4.start();
t2.start();
t3.start();
}
}
package com.thread.ticket3;
public class Ticket implements Runnable {
private static int tickets = 100;
//private final Object b = new Object();
private int x = 0;
@Override
public void run() {
while (true) {
if (x % 2 == 0) {
synchronized (this) {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
}
}
} else {
show();
}
x++;
}
}
//同步方法
private static synchronized void show() {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
}
}
}
上面在同步方法上加了 static 又导致了线程安全问题
如下图:
那么静态同步方法的对象又是什么呢??
默认当前类的字节码文件对象,所以要将同步代码块中的对象改为Ticket.class 同步方法和同步代码块中的对象一致。
synchronized (Ticket.class) {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
}
}
JDK1.5 的新特性
package com.thread.ticket4;
/**
* jdk1.5的新特性
* 以前同步关键字的方式,可以实现同步,并且是我们比较常见的方式,
* 但是我们老是需要去考虑它使用的哪个锁对象,比较麻烦
* 所以,在JDK5以后,提供了一个新的方式:
* Lock锁对象
* 这种方式在以前的代码中比较少见,但是以后开发中可以这样中
* vector:线程安全,加了synchronizd
* ArrayList:线程不安全
* hashTable:线程安全
* hashMap :线程不安全
*
* @author yuliyang
* @version $Id: TicketDemo.java, v 0.1 2016年12月4日 下午7:28:39 yuliyang Exp $
*/
public class TicketDemo {
public static void main(String[] args) {
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
package com.thread.ticket4;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Ticket implements Runnable {
private int ticket = 100;
//private final ReentrantLock lock = new ReentrantLock();
//定义一个锁对象
private final Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
try {
Thread.sleep(100);
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "---正在出售:" + (ticket--) + "张票");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放锁
lock.unlock();
}
}
}
}
生产者与消费者问题
package com.thread.ticket5;
/**
* 线程间的通信问题:
* 不同种类的线程对同一资源的操作问题。
*
* 需求:我有一个学生,我可以对其属性赋值,我也可以获取其属性值,请用线程间的通信的案例来体现
*
* 资源:学生student
* 获取线程:GetThread
* 设置线程:SetThread
* 测试类:StudentDemo
*
* 问题一:每个线程操作的是自己的那一个学生对象,
* 怎么解决:让多个线程操作同一个学生对象
* 问题二:因为线程的随机性,一次赋值和获取值得操作,数据出现了问题
* 怎么解决:
* 问题三:线程操作的东西一般都是比较复杂的操作
* 所以我们加入了循环操作。产生了更大的问题。
* 数据产生了混乱
* A:同一数据出现多次:
* CPU的一点点时间就足够一个线程执行很多次。
* B:数据出现了问题:
* 由于CPU每一次的执行点应该是一次原子性的操作。
* 解决线程安全问题:
* 分析:真的是线程安全问题吗?
* 1:是多线程环境吗?
* 是
* 2:有共享数据吗?
* 有:student
* 3:对共享数据有多条语句操作吗?
* 有:s.name s.age
*
* 如何解决?
* 把出现问题的代码用同步代码块包起来。
* 问题4:加入同步方法块之后还是出现了问题。为什么?
* A:多种线程进行操作的时候,要保证同步,必须是每一种类型的线程都必须加同步
* B:即使是给多种类型的线程加锁,也必须是同一把锁
* 到此解决了线程安全问题。
*
* @author yuliyang
* @version $Id: TicketDemo.java, v 0.1 2016年12月4日 下午7:28:39 yuliyang Exp $
*/
public class StudentDemo {
public static void main(String[] args) {
Student student = new Student();
SetThread st = new SetThread(student);
GetThread gt = new GetThread(student);
Thread g = new Thread(gt);
Thread s = new Thread(st);
s.start();
g.start();
}
}
package com.thread.ticket5;
public class Student {
private String name;
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
package com.thread.ticket5;
public class SetThread implements Runnable {
private final Student s;
private int x = 0;
public SetThread(Student s) {
this.s = s;
}
@Override
public void run() {
while (true) {
//加同步,加同一把锁(s)
synchronized (s) {
if (x % 2 == 0) {
s.setName("aaa");
s.setAge("20");
} else {
s.setName("vvv");
s.setAge("10");
}
}
x++;
}
}
}
package com.thread.ticket5;
public class GetThread implements Runnable {
private final Student student;
public GetThread(Student student) {
this.student = student;
}
@Override
public void run() {
while (true) {
//加同步,加同一把锁
synchronized (student) {
System.out.println(student.getName() + ":" + student.getAge());
}
}
}
}
等待唤醒机制
package com.thread.ticket6;
/**
* 线程间的通信问题:
* 不同种类的线程对同一资源的操作问题。
*
* 需求:我有一个学生,我可以对其属性赋值,我也可以获取其属性值,请用线程间的通信的案例来体现
*
* 资源:学生student
* 获取线程:GetThread
* 设置线程:SetThread
* 测试类:StudentDemo
*
* 问题一:店铺生产了包子,客人才可以买,卖完了,店铺再生产
* 刚才的数据是大片大片的数据,此时需要改为:只有设置了学生的属性,才能获
* 取到学生的属性值,依次的出现,怎么办呢?
* 原理:
* 如果我的学生属性没有值,应该先赋值,然后才可以使用,
* 如果我的学生属性有值,应该使用后再赋值。
* 在Student中加入了一个flag标记,false表示没有值
*
* 为了配合这种操作,java提供了一种机制:等待唤醒机制。
* notify(); //该方法在Object中
* wait(); //该方法在Object中
* 这两个方法的调用,必须是锁对象(对象监视器)调用。
* 为什么这两个方法没有定义到Thread类里面呢,
* 因为如果放到了Thread类中,就得使用Thread对象去调用这两个方法,
* 但是,同步代码块的锁对象是任意的。那么只能用Object。
* 怎么用?
* 看代码例子。
*
*/
public class StudentDemo {
public static void main(String[] args) {
Student student = new Student();
SetThread st = new SetThread(student);
GetThread gt = new GetThread(student);
Thread g = new Thread(gt);
Thread s = new Thread(st);
s.start();
g.start();
}
}
package com.thread.ticket6;
public class SetThread implements Runnable {
private final Student student;
private int x = 0;
public SetThread(Student s) {
this.student = s;
}
@Override
public void run() {
while (true) {
//加同步,加同一把锁(s)
synchronized (student) {
//如果有值:等待,(店家如果有包子,就等它卖完)
if (student.getFlag()) {
try {
student.wait();
/**
* s线程就等待在这里了。
*/
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//设置值(如果没有包子,就设置值)
if (x % 2 == 0) {
student.setName("余丽阳");
student.setAge("20");
} else {
student.setName("简小慧");
student.setAge("10");
}
x++;
//有值后,修改标记为true(告诉顾客有包子了)
student.setFlag(true);
//唤醒等待程序
student.notify();
/**
* 唤醒等待的线程,唤醒其他的线程,不代表其他的线程就能够立即执行
*/
}
//可能g线程,可能s线程
}
}
}
package com.thread.ticket6;
public class GetThread implements Runnable {
private final Student student;
public GetThread(Student student) {
this.student = student;
}
@Override
public void run() {
while (true) {
//加同步,加同一把锁
synchronized (student) {
//如果没有值,就等待。(顾客发现如果没有包子,就等着)
if (!student.getFlag()) {
try {
student.wait();
/**
* g线程在这里等待了,那么它就会释放锁资源。
* 将来,当它再次获取锁对象的时候,
* 是从哪里等待,哪里醒来
*/
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果有值,则输出(如果有包子,就买了吃)
System.out.println(student.getName() + ":" + student.getAge());
//告诉店家没有包子了
student.setFlag(false);
//唤醒:(叫店家做包子)
student.notify();
}
//可能g线程,可能s线程
}
}
}
package com.thread.ticket6;
public class Student {
private String name;
private String age;
private boolean flag = false; //默认表示没有值
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public boolean getFlag() {
return flag;
}
public void setFlag(boolean f) {
this.flag = f;
}
}
死锁
package com.thread.DieLock;
/**
* 举例:
* 五个哲学家的故事
*
* 面试题:
* 死锁例子:
* 有两把锁 A,B 一个线程先使用了A锁,然后使用B锁
* 一个线程先使用了B锁,然后使用了A锁。
*
* @author yuliyang
* @version $Id: DieLockDemo.java, v 0.1 2016年12月4日 下午9:59:10 yuliyang Exp $
*/
public class DieLockDemo {
public static void main(String[] args) {
DieLock d1 = new DieLock(true);
DieLock d2 = new DieLock(false);
d2.start();
d1.start();
}
}
package com.thread.DieLock;
public class DieLock extends Thread {
private final boolean flag;
public DieLock(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
synchronized (MyLock.objA) {
System.out.println("if objA");
synchronized (MyLock.objB) {
System.out.println("if objB");
}
}
} else {
synchronized (MyLock.objB) {
System.out.println("else objB");
synchronized (MyLock.objA) {
System.out.println("else objA");
}
}
}
}
}
package com.thread.DieLock;
public class MyLock {
public static final Object objA = new Object();
public static final Object objB = new Object();
}