1, 线程简介(略)
2, 线程锁
说明:
2.1) 两个主要特性: 互斥和可见性
互斥:即一次只允许一个线程持有某个特定的锁,可利用改特性实现对共享数据的协调访问,这样,一次就只有个一个线程使用该共享数据。
可见性:即线程在释放锁之后对共享变量的修改对之后获取该锁的线程是可见的
2.2) 使用synchronized或volatile,
synchronized:
public class Account { private float totalMoney; public synchronized void saveMoney(float money) { this.totalMoney += money; } public synchronized void takeMoney(float money) { if(this.totalMoney - money >= 0) { this.totalMoney -= money; } } }
说明: 对存钱,取钱方法进行加锁:当有一个线程A调用存钱时,其他的存钱线程必须被阻塞,待A结束后,其他线程才能继续,即有且只能有一个线程调用存钱方法,取钱操作亦如此。
volatile:
说明:volatile具有synchronized的可见性特性,但不具备互斥性
使用条件:介于volatile不预备原子性,所以在多线程写操作较频繁时,不适用使用volatile修饰变量,因为volatile的可见性(对共享变量的修改可直接被用于其他线程),所以在写操作较少,读操作较多的时候,可以考虑使用volatile
使用场景:比如有某个变量V,存储的是从数据库读取的配置参数,其他线程要读取这个配置参数进行不同的操作,另外有一个线程是每隔一段时间从数据库读取配置信息赋予V, 这个变量V可以声明为volatile,确保每次读取最新配置后,其它线程使用的都是最新的。
3, 死锁
说明:最常见的死锁形式是当线程1持有对象A上的锁,而且正在等待B上的锁释放,线程2持有对象B上的锁,但在等待A上的锁释放,这样线程1和线程2就会一直处于等待状态,因为两者都不会主动释放自己的锁,那么就称他们被死锁了。
例子:
public class Deadlock extends Thread{ private static Object lock1 = new Object(); private static Object lock2 = new Object(); private int mentod_flag = 1; public Deadlock(int flag) { this.mentod_flag = flag; } void a () { synchronized(lock1) { System.out.println("a(), lock1 is synchronized"); try { Thread.sleep(1000); }catch (Exception e) { e.printStackTrace(); } synchronized(lock2) { System.out.println("a(), lock2 is synchronized"); System.out.println("a(), lock1: "+lock1+" | lock2: "+ lock2); } } } void b () { synchronized(lock2) { System.out.println("b(), lock2 is synchronized"); try { Thread.sleep(1000); }catch (Exception e) { e.printStackTrace(); } synchronized(lock1) { System.out.println("b(), lock1 is synchronized"); System.out.println("b(), lock1: "+lock1+" | lock2: "+ lock2); } } } public void run() { if(this.mentod_flag == 1) { this.a(); }else { this.b(); } } public static void main(String[] args) { new Deadlock(1).start(); new Deadlock(2).start(); } }
4,线程调度
join(), wait(), notify(), notifyAll();
4.1) wait()和notify(), notifyAll()协同使用
Object类定义了wait(), notify(), notifyAll()方法;要执行这些方法,必须拥有相关对象的锁
wait()会让调用的线程休眠,直到用Thread.interrupt(), 过了等待的时间(wait(long timeout)), 另一个线程用notify()或notifyAll()唤醒他
当对某个对象调用notify时,如果有任何线程正在通过wait()等待该对象,那么就会唤醒其中一个线程,如果调用notifyAll(), 就会唤醒所有正在等待该对象的线程。
使用场景:比较有代表性的是生产者和消费者例子:生产者线程A负责生产产品,当产品数量达到指定上限,停止生产,消费者线程B负责消费产品,但产品数量达到0时停止消费,并通知A生产产品。
例子:
public class Product { private Long productNo; public Long getProductNo() { return productNo; } public Product(Long productNo) { this.productNo = productNo; } } public class PublicResource { private List<Product> products = new ArrayList<Product>(); public List<Product> getProducts() { return this.products; } public synchronized void add() { while(products.size() >= 20) { try { this.wait(); } catch (Exception e) { e.printStackTrace(); } } Product p = new Product(System.currentTimeMillis()); this.products.add(p); System.out.println("add product, productNo: "+p.getProductNo()); this.notify(); } public synchronized void remove() { while(products.size() <= 0) { try { this.wait(); } catch (Exception e) { e.printStackTrace(); } } Product p = this.products.remove(0); System.out.println("remove product, productNo: "+p.getProductNo()); this.notify(); } } public class ProducerThread implements Runnable { private PublicResource _resource; public ProducerThread(PublicResource resource) { this._resource = resource; } @Override public void run() { for(int i=0;i<10;i++) { try { Thread.sleep((long)(Math.random()*1000)); } catch (Exception e) { e.printStackTrace(); } this._resource.add(); } } } public class ConsumerThread implements Runnable { private PublicResource _resource; public ConsumerThread(PublicResource resource) { this._resource = resource; } @Override public void run() { for(int i=0;i<10;i++) { try { Thread.sleep((long)(Math.random()*1000)); } catch (Exception e) { e.printStackTrace(); } this._resource.remove(); } } } public class ProductMain { public static void main(String[] args) throws InterruptedException { PublicResource resource = new PublicResource(); new Thread(new ProducerThread(resource)).start(); new Thread(new ConsumerThread(resource)).start(); Thread.sleep(10000); System.out.println(resource.getProducts().size()); } }
4.2) join()使用
说明:当t1调用线程t2.join()时,t1将被阻塞,直到t2线程执行完为止。
可以使用t2.join(timeout) 限制t2线程的堵塞时间。
例子:
public class JoinTest { public static void main(String[] args) { Sleeper xmSleeper = new Sleeper("xmSleeper",1500), ywSleeper = new Sleeper("ywSleeper",1500); Joiner syJoiner = new Joiner("syJoiner", xmSleeper), lhJoiner = new Joiner("lhJoiner", ywSleeper); ywSleeper.interrupt(); } } class Sleeper extends Thread { private int duration; public Sleeper(String name, int sleepTime) { super(name); duration = sleepTime; start(); } public void run() { try { sleep(duration); } catch (InterruptedException e) { System.out.println(getName()+" was interrupted. "+"isInterrupted(): "+isInterrupted()); return; } System.out.println(getName()+" has awakened"); } } class Joiner extends Thread { private Sleeper sleeper; public Joiner(String name, Sleeper sleeper) { super(name); this.sleeper = sleeper; start(); } public void run() { try { sleeper.join(); } catch (InterruptedException e) { System.out.println("Interrupted"); } System.out.println(getName() + " join completed"); } }
4.3) yield()方法
说明:若在某个线程调用Thread.yield(),此时这个线程会让出cpu资源,留给下一个线程执行,下一个线程不确定,有可能是他自己,也有可能是另一个线程。
例子:
public class YieldTest { public static void main(String[] args) { ThreadTest1 t1 = new ThreadTest1(); t1.start(); ThreadTest2 t2 = new ThreadTest2(); t2.start(); } public static class ThreadTest1 extends Thread { public void run() { for(int i=0;i<10;i++) { System.out.println("ThreadTest1: 第"+i+"次运行"); Thread.yield(); } } } public static class ThreadTest2 extends Thread { public void run() { for(int i=0;i<10;i++) { System.out.println("ThreadTest2: 第"+i+"次运行"); } } } } //output: ThreadTest1: 第0次运行 ThreadTest1: 第1次运行 ThreadTest1: 第2次运行 ThreadTest2: 第0次运行 ThreadTest1: 第3次运行 ThreadTest2: 第1次运行 ThreadTest1: 第4次运行 ThreadTest2: 第2次运行 ThreadTest2: 第3次运行 ThreadTest2: 第4次运行
5. 守护线程
说明:比如我们在main方法中启动一个线程,当mian方法中所有的代码执行完成后,若此线程仍没结束,他仍会在后台运行,怎样控制当main方法运行完毕,同时此线程也结束,就要用到守护线程。
例子:
public class SimpleDaemons { public static void main(String[] args) throws InterruptedException { ThreadTest t = new ThreadTest(); t.setDaemon(true); t.start(); Thread.sleep(5000); } public static class ThreadTest extends Thread { public void run() { while(true) { System.out.println("ThreadTest: print something"); try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } } } } }
说明:当main函数休眠5秒结束后,ThreadTest也会结束。