线程学习笔记(下)
同步
- 由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问
- 由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:synchronized方法synchronized块
同步:也称为并发,由于有多个线程访问同一份资源,所以确保资源安全 -->线程安全 >eg:HashTable是线程安全的,而HashMap是线程不安全的
>eg:StringBuffer是线程安全的,而StringBuild是线程不安全的
一、 同步块
synchronized(引用类型|this|类.class){}
二、 同步方法 > public synchronized void test(){}
而同步块中的类.class涉及到单例设计模式
单例设计模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
class Jvm{ private static Jvm instance = null;
private Jvm(){
} public static Jvm getInstance(long time){ if (null == instance) { try { Thread.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } instance = new Jvm(); } return instance; } }
|
死锁
过多的同步容易造成死锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
|
public class SynDemo03 { public static void main(String[] args){ Object goods = new Object(); Object money = new Object(); Test t1 = new Test(goods,money); Test2 t2 = new Test2(goods,money); Thread proxy = new Thread(t1); Thread proxy2 = new Thread(t2); proxy.start(); proxy2.start(); } }
class Test implements Runnable{ Object goods; Object money;
public Test(Object goods, Object money) { this.goods = goods; this.money = money; }
@Override public void run() { while(true){ test(); } } public void test(){ synchronized (goods){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (money){
} }
System.out.println("一手给钱"); }
}
class Test2 implements Runnable{ Object goods ; Object money ;
public Test2(Object goods, Object money) { this.goods = goods; this.money = money; }
@Override public void run() { while(true){ test(); } } public void test(){ synchronized (money){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (goods){
} }
System.out.println("一手给货"); } }
|
生产者消费者模式(不是设计模式而是解决死锁的一种方式)
- 生产者消费者问题,也称有限缓冲问题,是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程-即所谓的“生产者”和“消费者”-在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗说这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
- 要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。通常常用的方法有信号灯法、管程等。如果解决方法不够完善,则容易出现死锁情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
|
public class Movie { private String pic ; private boolean flag = true; public synchronized void play(String pic){ if(!flag){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } this.pic = pic; this.notify(); this.flag = false; } public synchronized void watch(){ if(flag){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(pic); this.notifyAll(); this.flag = true; }
public static void main(String[] args){ Movie m = new Movie();
Player p = new Player(m); Watcher w = new Watcher(m);
new Thread(p).start(); new Thread(w).start(); } }
class Player implements Runnable{ private Movie m;
public Player(Movie m) { this.m = m; }
@Override public void run() { for(int i=0;i<20;i++){ if(i%2==0){ m.play("偶数"); }else{ m.play("奇数"); } } } } class Watcher implements Runnable{ private Movie m;
public Watcher(Movie m) { this.m = m; }
@Override public void run() { for(int i=0;i<20;i++){ m.watch(); } } }
|
任务调度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
public class TimeDemo01 { public static void main(String[] args){ Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { System.out.println("hello world"); } },new Date(System.currentTimeMillis()+1000),200); } }
|