Java中的死锁现象及线程间的通信
一、死锁现象:
两个或者两个以上的线程再争夺资源的过程中,发生的一种相互等待的现象
案例:
class MyClock{
//创建锁对象
public static final Object objA = new Object();
public static final Object objB = new Object();
}
class DieLock extends Thread{
private boolean flag;
public DieLock(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if(flag) {
//同步嵌套
synchronized (MyClock.objA) {
System.out.println("if里的objA");
synchronized (MyClock.objB) {
System.out.println("if里的objB");
}
}
}else {
synchronized (MyClock.objB) {
System.out.println("else里的objB");
synchronized (MyClock.objA) {
System.out.println("else里的objA");
}
}
}
}
}
public class DieLockDemo {
public static void main(String[] args) {
DieLock d1 = new DieLock(true);
DieLock d2 = new DieLock(false);
d1.start();
d2.start();
}
}
二、线程间的通信问题:
不同种类的线程间针对同一资源的操作
就像生产者生产出的西瓜供消费者消费,生产者和消费者都对西瓜进行了操作,这就实现了生产者和消费者之间的通信
线程对同一学生类进行操作:
package Demo;
//创建学生类
class Student {
public String name;
public int age;
}
//给学生类赋值(生产者)
class SetThread implements Runnable {
Student s;
public SetThread(Student s) {
this.s = s;
}
int x = 0;
@Override
public void run() {
while(true) {
synchronized (s) {
if(x % 2 == 0) {
s.name = "lyr";
s.age = 18;
}else {
s.name = "kx";
s.age = 20;
}
x++;
}
}
}
}
//输出学生类的值(消费者)
class GetThread implements Runnable {
Student s;
public GetThread(Student s) {
this.s = s;
}
@Override
public void run() {
while(true) {
synchronized (s) {
System.out.println(s.name+"---"+s.age);
}
}
}
}
public class StudentDemo {
public static void main(String[] args) {
Student s = new Student();
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt);
t1.start();
t2.start();
}
}
但是这种生产者会一直产生数据,不管消费者是否已经使用
正常的操作:
生产者:
先看是否有数据,有就等待,没有就生产
消费者:
先看是否有数据,有就使用,没有就等待
如何实现:
通过Java提供的等待唤醒机制
Object类提供了三个方法:
wait():等待
notify:唤醒单个线程
notifyall:唤醒全部线程
改进后的代码:
package Demo;
//创建学生类
class Student {
private String name;
private int age;
private boolean flag;
public synchronized void set(String name,int age) {
if(this.flag) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.name = name;
this.age = age;
this.flag = true;
this.notify();
}
public synchronized void get() {
if(!this.flag) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(this.name + "----" + this.age);
this.flag = false;
this.notify();
}
}
//给学生类赋值
class SetThread implements Runnable {
Student s;
public SetThread(Student s) {
this.s = s;
}
int x = 0;
@Override
public void run() {
while(true) {
if(x % 2 == 0) {
s.set("lyr", 18);
}else {
s.set("kx", 20);
}
x++;
}
}
}
//输出学生类的值
class GetThread implements Runnable {
Student s;
public GetThread(Student s) {
this.s = s;
}
@Override
public void run() {
while(true) {
s.get();
}
}
}
public class StudentDemo {
public static void main(String[] args) {
Student s = new Student();
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt);
t1.start();
t2.start();
}
}
三、线程池:
1、线程池的好处:
线程池里的每一个线程代码结束后,并不会死亡,而是回到线程池中成为空闲状态,等待下一个对象来使用
线程池的使用:
package Demo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class MyRunnable implements Runnable{
@Override
public void run() {
for(int x = 0;x < 100;x ++) {
System.out.println(Thread.currentThread().getName() + ":" + x);
}
}
}
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.shutdown();
}
}
四、Callable实现多线程:
Callable:带泛型的接口
依赖线程池实现多线程,所以有局限性
package Demo;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class MyCallable implements Callable{
@Override
public Object call() throws Exception {
for(int x= 0;x < 100 ; x ++) {
System.out.println(Thread.currentThread().getName()+":"+x);
}
return null;
}
}
public class CallableDemo {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.submit(new MyCallable());
pool.submit(new MyCallable());
pool.shutdown();
}
}