一、对象锁/同步锁/互斥锁
每个对象都有一个自己的同步锁,当一个线程调用了对象的synchronized修饰的方法或语句块时,这个线程就获得 了对象的同步锁,其他线程如果这时调用了对象的synchronized修饰的方法或语句块时,将等待,当线程执行完 synchronized方法或语句块时,释放锁。
public class Account {
private int money = 20000;
public synchronized void add() {
money+=1000;
System.out.println("存了1000元,余额为:"+money);
}
public synchronized void sub() {
money-=1000;
System.out.println("取了1000元,余额为:"+money);
}
public static void main(String[] args) {
Account account = new Account();
AddRunnable ar = new AddRunnable(account);
SubRunnable sr = new SubRunnable(account);
Thread t1 = new Thread(ar);
Thread t2 = new Thread(sr);
t1.start();
t2.start();
}
}
语句块方式:
public void add() {
//多线程执行到synchronized时,要获得哪个对象的同步锁
synchronized (this) {
money+=1000;
System.out.println("存了1000元,余额为:"+money);
}
}
两个线程就实现了同步,Account对象是线程安全的
二、死锁
两个线程互相等待对方释放同步锁,造成程序永久等待的情况。死锁是不可预知的,通过编程尽量控制死锁的产 生,例如加锁顺序。
public class DeadLock implements Runnable{
private static Object obj1 = new Object();
private static Object obj2 = new Object();
public boolean flag = true;
@Override
public void run() {
while(true) {
if(flag) {
synchronized (obj1) {
synchronized (obj2) {
System.out.println("obj1 -> obj2");
}
}
}else {
synchronized (obj1) {
synchronized (obj2) {
System.out.println("obj1 -> obj2");
}
}
}
}
}
public static void main(String[] args) {
DeadLock d1 = new DeadLock();
DeadLock d2 = new DeadLock();
d2.flag = false;
Thread t1 = new Thread(d1);
Thread t2 = new Thread(d2);
t1.start();
t2.start();
}
}
三、线程八锁
1. 线程获得对象锁后,如果sleep,不释放对象锁
2. 线程调用synchronized方法或语句块时,获得对象锁;调用普通方法不需要获得对象锁;
3. 线程调用synchronized的静态方法,获得的是类锁(加载的class对象的对象锁)
四、线程通信
1. wait方法:是Object类的方法,意味着所有对象都有wait方法,调用了一个对象的wait方法,获得这个对象锁 的线程将等待,并释放对象锁。调用对象的wait方法时,一定要有一个线程获得对象的对象锁,否则抛出异常
2. notify方法:是Object类的方法,调用一个对象的notify方法,在这个对象上等待的线程中随机唤醒一个。
3. nofifyAll方法:在这个对象上等待的线程全都唤醒。
五、sleep和wait的区别:
1. sleep是Thread类的方法,wait是Object类的方法
2. sleep不释放对象锁,wait释放对象锁
3. sleep只能到指定的时间自动唤醒,wait可以指定时间,也可以通过notify主动唤醒
六、示例:生产者和消费者模式
生产者负责生产,消费者负责消费,生产/消费的资源保持一个平衡
public class Chicken {
private int count ; //count保持在10
public synchronized void add() {
//生产者线程 ,当count>10,让生产者线程等待,唤醒消费者线程
while(count>10) {
try {
this.wait(); //等待,被唤醒时,执行下面的代码
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count++;
this.notifyAll();
System.out.println("生产了一只烧鸡,柜台剩余:"+count);
}
public synchronized void sub() {
//消费线程 ,当count<10,让消费者线程等待,唤醒生产者线程
while(count<10) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
this.notifyAll();
System.out.println("消费了一只烧鸡,柜台剩余:"+count);
}
public static void main(String[] args) {
Chicken chicken = new Chicken();
ProductRunnable pr = new ProductRunnable(chicken);
ConsummerRunnable cr = new ConsummerRunnable(chicken);
new Thread(pr).start();
new Thread(pr).start();
new Thread(pr).start();
new Thread(pr).start();
new Thread(cr).start();
new Thread(cr).start();
new Thread(cr).start();
new Thread(cr).start();
new Thread(cr).start();
new Thread(cr).start();
}
}
七、线程池:
在项目中可能会创建大量的多线程,在执行完多线程后,线程对象被销毁,又有多线程需求时,再次创建多线程, 要反复的创建、销毁线程。可以用线程池管理多线程。
public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 10,
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(6),new ThreadPoolExecutor.DiscardPolicy());
for(int i = 1;i<=11;i++) {
pool.execute(new TestRunnable(String.valueOf(i)));
}
//pool.shutdown();
}
线程池的工作过程
创建线程池时,要指定核心线程数、最大线程数、存活时间、时间单位、拒绝策略参数。当线程池开始执行任务 时,创建多线程执行任务,当线程数达到了核心线程数,且都在执行任务,线程池执行新任务时,这个任务将存储 到队列中,当队列满时,线程池执行新任务时,再创建新的线程执行新的任务,要保证线程数不能超过最大线程 数。如果达到了最大线程数、队列也满了,再来任务时,将采取拒绝策略(四种,放弃任务抛异常/不抛异常,当 前执行execute的线程执行这个任务,将队列中最久的任务放弃执行新的任务)。当线程数大于核心线程数,且线 程空闲且达到了设置的存活时间,这个线程被销毁。
本文深入探讨了Java中的多线程概念,包括对象锁、同步锁和互斥锁的使用,避免死锁的策略,线程间的wait、notify和notifyAll通信机制,以及生产者消费者模式的应用。同时,介绍了线程池的概念和工作原理,强调其在管理大量线程时的重要性。
244

被折叠的 条评论
为什么被折叠?



