lock锁:接口
使用其子类对象
为保证被同步的代码块不出现异常,通常需要将解锁的过程放在final中,所以被同步的代码块也要放在try中
线程死锁:
当多个锁出现相互等待都不执行时,就出现线程死锁
死锁一般出现在锁的嵌套中,相互等着对方执行完毕,也就是双方都要执行对方已经加锁的代码
代码演示:
public class DeadLock extends Thread {
// 定义一个标记用来指定要执行的代码
boolean flag;
// 创建两个不同的锁对象
static Object o1 = new Object(); // 静态修饰的原因是保证锁的同步性!重要!
static Object o2 = new Object();
/**
* @param flag
*/
public DeadLock(boolean flag) {
super();
this.flag = flag;
}
@Override
public void run() {
super.run();
if (flag) { //创建线程对象时,将flag赋值为true时,执行的代码
synchronized (o1) {
System.out.println("if语句中o1的执行代码");
synchronized (o2) {
System.out.println("if语句中o2的执行代码");
}
}
}else { //创建线程对象时,将flag赋值为false时,执行的代码
synchronized (o2) {
System.out.println("else语句中o2的执行代码");
synchronized (o1) {
System.out.println("else语句中o1的 执行代码");
}
}
}
}
}
结合如下死锁测试代码,死锁过程为,当线程一进入之后判断条件语句,执行判断结果为true的同步锁代码,打印if中的语句,同时线程二进入判断条件语句,执行判断结果为false的同步锁代码,执行if中的语句,当两个线程继续向下执行时,各自的线程都没有出锁,而接下来的代码就是要执行对方线程的解锁后的代码,所以两个线程就互相 等待对方 执行完解锁,所以就形成了相互等待的死锁状态
死锁测试:
public class Test {
public static void main(String[] args) {
// 创建多线程对象
DeadLock lock1 = new DeadLock(true);
DeadLock lock2 = new DeadLock(false);
// 开启线程
lock1.start();
lock2.start();
}
}
解决方案:
不出现锁的嵌套
满足条件后自己解锁,比如当线程等待时间超过一定时间,就自动杀掉线程
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
生产者消费者:
生产者与消费者共享同一个资源
满足生产者生产条件时,生产者生产
当满足消费者消费条件时,消费者消费
产者与消费者可以通过不同的线程完成相应的动作
等待唤醒机制:
要执行的线程由于某种需求需要别的线程对共享数据先进行操作时,这个线程就需要等待
当其他的线程操作完之后,应该唤醒刚才那个等待的线程
等待唤醒机制涉及到的类:
Thread:多个线程才会出现呢等待唤醒机制
锁:等待唤醒机制需要同步代码,既然有同步代码就需要锁(synchronize)
锁是Object类型:是线程等待,但是是线程通过锁等待
代码实现:
1.创建共享数据类
public class Person {
// 封装成员属性
private String name;
private int age;
private boolean flag;
// 提供判断条件用于区分生产者和消费者的执行路线
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) { // 对外提供公共的 修改标志条件的入口,是保证生产和消费一一对应的前提
this.flag = flag;
}
public Person() { // 创建空参的 构造函数用于创建共享对象的数据
super();
// TODO Auto-generated constructor stub
}
/**
* @param name
* @param age
*/
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
// 对外提供公共的访问入口
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
2.创建生产者线程类
public class Set implements Runnable {
int i=0;
private Person p;
/**
* @param p
*/
public Set(Person p) { // 为什么呢? 因为创建线程的时候保证了生产者和消费者使用的是同一个对象
super();
this.p = p;
}
// 重写run方法
public void run() {
while (true) {
synchronized (p) { // 保证所共享的数据是同步的
if (p.isFlag()) {
try {
p.wait(); // 先进行判断,如果满足条件则生产,否则等待消费者消费
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 判断生产的元素
if (i%2==0) {
p.setName("龙女");
p.setAge(16);
System.out.println(Thread.currentThread().getName()+p);
}else {
p.setName("过儿");
p.setAge(18);
System.out.println(Thread.currentThread().getName()+p);
}
i++; //制造不同的循环生产条件
p.setFlag(true); // 改变标志,让消费者消费
p.notify(); // 唤醒消费者
}
}
}
}
3.创建消费者线程类
public class Get implements Runnable {
private Person p;
/**
* @param p
*/
public Get(Person p) { // 利用出入的对象作为共享数据源
super();
this.p = p;
}
// 重写run方法
public void run() {
while (true) {
synchronized (p) {
// 判断标志
if (!p.isFlag()) {
try {
p.wait(); // 判断,如果满足条件则消费,否则等待生产者生产
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+p);
}
p.setFlag(false); // 改变标志,让生产线程执行
p.notify(); // 唤醒生产线程
}
}
}
}
4.测试生产者和消费者一一对应的运行结果
public class Test {
public static void main(String[] args) {
// 创建共享对象数据
Person p = new Person();
Set set = new Set(p);
Get get = new Get(p);
// 创建线程对象
Thread setThread = new Thread(set,"生产者");
Thread getThread = new Thread(get,"消费者");
// 同时开启生产者和消费者线程
setThread.start();
getThread.start();
}
}
据的同步性,当生产者和消费者执行各自的线程时首先要进行判断,如果不满足条件则等待对方对共享数据的操作,然后改变判断条件,唤醒等待,如此反复执行下去
线程组:
用来组织线程
线程池:
程序在开辟新的线程时,要消耗大量的系统资源,频繁的创建及销毁线程会大大降低程序的效率
在程序开始时,只要创建一个线程池,将指定数量的线程开启,如果使用,就使用线程池中的线程,如果不使用,线程也不销毁,而是出于空闲状态,等待被再次使用
线程池可以线程的某些行为,比如销毁动作