一、死锁
1.概述
- 死锁 : 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法往下执行。
- 此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程
- 原理 :
-
- 某个线程执行完成,需要先后嵌套锁定两个对象,在这个过程中,先锁定了第一个对象
- 另一个线程执行完成也需要先后嵌套锁定两个对象,在这个过程中,先锁定了第二个对象
- 第一个线程执行中,要执行到第二个对象的时候,发现第二个对象被锁定,进入等待状态,等待交出锁
- 第二个线程执行中,要执行到第一个对象的时候,发现第一个对象也被锁定,也进入等待状态
- 此时两个线程都在等待对方交出锁,导致死锁
2.代码实现
public class Thread_01_DeadLock {
public static void main(String[] args) {
Object o1=new Object();
Object o2=new Object();
Thread t1=new Thread(new T1(o1, o2));
Thread t2=new Thread(new T2(o1, o2));
t1.start();
t2.start();
}
}
class T1 implements Runnable{
Object o1;
Object o2;
public T1(Object o1,Object o2){
this.o1=o1;
this.o2=o2;
}
@Override
public void run() {
synchronized (o1) {
System.out.println(Thread.currentThread().getName()+"-->T1o1已锁定");
synchronized (o2) {
System.out.println(Thread.currentThread().getName()+"-->T1o2已锁定");
}
}
System.out.println("t1执行完成");
}
}
class T2 implements Runnable{
Object o1;
Object o2;
public T2(Object o1,Object o2){
this.o1=o1;
this.o2=o2;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2) {
System.out.println(Thread.currentThread().getName()+"-->T2o2已锁定");
synchronized (o1) {
System.out.println(Thread.currentThread().getName()+"-->T2o1已锁定");
}
}
System.out.println("t2执行完成");
}
}
二、线程通信
1.概述
- Object中的方法
- wait : 让当前线程进入等待状态(挂起),并释放锁,当被唤醒之后,接着挂起的位置继续执行,假如之前执行了1、2,到3挂起,那么被唤醒后接着执行3
- notify : 唤醒一个在该对象中挂起的任意一个线程
- notifyAll : 唤醒在该对象中挂起的所有线程
- 这几个方法必须出现在加锁的成员方法中
- wait : 如果是无参,则不会自动醒,也可以传入long类型的值,代表毫秒数,多久之后自动醒
- wait 和 sleep的区别 :
-
- sleep : 让当前线程进入睡眠状态, 是静态方法,和是否加锁没有关系,如果在加锁的方法中,也不会释放锁
- wait : 让当前线程进入挂起等待状态,必须在加锁的成员方法中,另外会释放锁
2.使用方式
public class Thread_03_Wait {
public static void main(String[] args) throws InterruptedException {
Num num=new Num();
Thread t1=new PrintNum(num);
Thread t2=new PrintNum(num);
t1.start();
Thread.sleep(10);
t2.start();
}
}
class PrintNum extends Thread{
Num num;
public PrintNum(Num num){
this.num=num;
}
@Override
public void run() {
while (true) {
num.printNums();
}
}
}
class Num{
private int count =1;
public synchronized void printNums(){
System.out.println(Thread.currentThread().getName()+"-->"+count);
count++;
this.notifyAll();
try {
Thread.sleep(1000);
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3.生产者消费者
3.1.示例
public class Thread_04_Producer {
public static void main(String[] args) {
SynStack ss=new SynStack();
Thread producer1=new Thread(new Producer(ss));
Thread producer2=new Thread(new Producer(ss));
Thread consumer1=new Thread(new Consumer(ss));
Thread consumer2=new Thread(new Consumer(ss));
producer1.start();
producer2.start();
consumer1.start();
consumer2.start();
}
}
class Producer implements Runnable{
private SynStack ss;
public Producer(SynStack ss){
this.ss=ss;
}
@Override
public void run() {
for (int i = 0; i < 26; i++) {
ss.push((char)('a'+i));
}
}
}
class Consumer implements Runnable{
private SynStack ss;
public Consumer(SynStack ss){
this.ss=ss;
}
@Override
public void run() {
for (int i = 0; i < 26; i++) {
ss.pop();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class SynStack{
int count=0;
char[] data=new char[6];
public synchronized void push(char ch){
while(count ==data.length){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (count==0) {
this.notifyAll();
}
data[count++]=ch;
System.out.println(Thread.currentThread().getName()+"生产了 "+ch+" 还剩 "+count+" 个货物");
}
public synchronized char pop(){
while(count ==0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (count==data.length) {
this.notifyAll();
}
char ch=data[--count];
System.out.println(Thread.currentThread().getName()+"消费了 "+ch+" 还剩 "+count+" 个货物");
return ch;
}
}
三、单例模式
public class SingLeton {
private SingLeton(){
}
private volatile static SingLeton singLeton;
public static SingLeton getInstance(){
if (singLeton==null) {
synchronized (SingLeton.class) {
if (singLeton==null) {
singLeton=new SingLeton();
}
}
}
return singLeton;
}
}
四、线程池
- 线程池的作用:
-
- 根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;
- 少了浪费了系统资源,多了造成系统拥挤效率不高。
- 用线程池控制线程数量,其他线程排队等候。
- 一个任务执行完毕,再从队列的中取最前面的任务开始执行。
- 若队列中没有等待进程,线程池的这一资源处于等待。
- 当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了,否则进入等待队列。
- 为什么要用线程池:
-
- 减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
- 可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)