admin 管理员组

文章数量: 1086019

线程同步锁

一、对象锁/同步锁/互斥锁

每个对象都有一个自己的同步锁,当一个线程调用了对象的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;@Overridepublic 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保持在10public 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的线程执行这个任务,将队列中最久的任务放弃执行新的任务)。当线程数大于核心线程数,且线 程空闲且达到了设置的存活时间,这个线程被销毁。

本文标签: 线程同步锁