Java笔记 - 线程与并行API

一、线程简介

1、线程与进程

     每个进程都具有独立的代码和数据空间,进程间的切换会有较大的开销。线程是轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。

          多进程:在操作系统中能同时运行多个任务(程序)

          多线程:在同一应用程序中有多个顺序流同时执行

2、线程的应用


二、线程状态控制

      线程具有创建、就绪、运行、阻塞、终止,五种状,详细的状态转换如下图所示:

                 image

1、线程的创建与启动

   JVM启动时会有一个由主方法所定义的线程,程序员可以通过实现 Runable接口的类 Thread类 的实例创建新的线程,每个线程对象都是通过方法run()来完成其操作,通过start()方法来启动一个线程。

(1)定义线程类实现Runable接口【建议

       使用Runable接口可以更灵活的定义多线程,如:为多个线程提供共享的数据(线程同步问题)另外,在实现Runable接口的类的run方法定义中可以使用Thread的静态方法。

  1 public class Main {
  2 	public static void main(String args[]) {
  3 		Runner1 r = new Runner1();
  4 		Thread t = new Thread(r);
  5 
  6 		t.start();//r.run()是方法调用,而非线程启动
  7 
  8 		for(int i=0; i<100; i++) {
  9 			System.out.println("Main Thread:------" + i);
 10 		}
 11 	}
 12 }
 13 
 14 //同一个Runable可以创建多个Thread
 15 class Runner1 implements Runnable {
 16 	public int a = 0;
 17 	public void run() {//定义线程体
 18 		for(int i=0; i<100; i++) {
 19 			System.out.println("Runner1 :" + i);
 20 		}
 21 	}
 22 }

(2)定义Thread的子类,并重写run()方法

  1 public class Main {
  2 	public static void main(String args[]) {
  3 
  4 		Runner1 r = new Runner1();
  5 		r.start();
  6 
  7 		for(int i=0; i<100; i++) {
  8 			System.out.println("Main Thread:------" + i);
  9 		}
 10 	}
 11 }
 12 
 13 //Thread类已经实现了Runable接口
 14 class Runner1 extends Thread {
 15 	public void run() {
 16 		for(int i=0; i<100; i++) {
 17 			System.out.println("Runner1 :" + i);
 18 		}
 19 	}
 20 }
 21 

(3)如何关闭一个线程

  1 public class Main {
  2 	public static void main(String args[]){
  3 		Runner r = new Runner();
  4        	Thread t = new Thread(r);
  5         t.start(); //线程启动
  6 
  7         for(int i=0;i<10;i++){
  8         	if(i%2==0 & i>0)
  9         		System.out.println("in thread main i=" + i);
 10         }
 11         System.out.println("Thread main is over");
 12 
 13         //stop()与interrupt()会立即关闭线程,造成正在打开的资源无法关闭,不建议使用
 14         r.shutDown();
 15     }
 16 }
 17 //设置flag标志位,表示线程是否关闭
 18 class Runner implements Runnable {
 19   private boolean flag=true;
 20 
 21 	public void run() {
 22 		int i = 0;
 23 		while (flag==true) {
 24 			System.out.println(" " + i++);
 25 		}
 26 	}
 27 
 28   public void shutDown() {
 29 		flag = false;
 30   }
 31 }

2、线程的基本操作

    线程控制基本方法如下:

                image

(1)线程优先级

        JAVA提供一个线程调度器来监视程序中启动后进入就绪状态的所有线程。线程调度器按照线程的优先级决定调度那个线程来执行。线程的优先级用户数字表示,范围从1到10,线程的缺省优先级是5。

                  Thread.MIN_PRIORITY = 1
                   Thread.NORM_PRIORITY = 5
                   Thread.MAX_PRIORITY = 10

  1 public class Main {
  2 	public static void main(String[] args) {
  3 		Thread t1 = new Thread(new T1());
  4 		Thread t2 = new Thread(new T2());
  5 
  6 		System.out.println(t1.getPriority()); //获取线程对象的优先级
  7 		t1.setPriority(Thread.NORM_PRIORITY + 3);//设置线程对象的优先级
  8 		t1.start();
  9 		t2.start();
 10 	}
 11 }
 12 
 13 class T1 implements Runnable {
 14 	public void run() {
 15 		for(int i=0; i<100; i++) {
 16 			System.out.println("T1: " + i);
 17 		}
 18 	}
 19 }
 20 
 21 class T2 implements Runnable {
 22 	public void run() {
 23 		for(int i=0; i<100; i++) {
 24 			System.out.println("------T2: " + i);
 25 		}
 26 	}
 27 }

(2)线程wait与sleep方法

      wait是Object的方法,调用wait方法时必须锁定对象,wait时别的线程可以访问锁定的对象,wait需要notify或notifyALL方法唤醒。

      sleep是Thread静态方法,sleep时别的方法不能访问锁定的对象,sleep方法等睡眠时间到了,可以自己苏醒。

  1 import java.util.*;
  2 public class Main {
  3   public static void main(String[] args) {
  4     MyThread thread = new MyThread();
  5     thread.start();//每隔一秒输出一次时间
  6 
  7     try {
  8     	//在未继承Thread类的方法在可以调用Thread的静态方法sleep暂停进程
  9     	Thread.sleep(10000);
 10     	} catch (InterruptedException e) {
 11     		e.printStackTrace();
 12     	}//主线程等待10s
 13 
 14     thread.interrupt();//不建议使用interrupt和stop
 15   }
 16 }
 17 
 18 class MyThread extends Thread {
 19 	boolean flag = true;
 20 	public void run(){
 21 		while(flag){
 22 			//若调用该线程的主线程还“活着”
 23 			if(Thread.currentThread().isAlive()) {
 24 				System.out.println("==="+new Date()+"===");
 25 			}
 26 
 27 			try {
 28 	                //public static sleep(long millis)throw InterruptedException
 29 			//使得当前线程休眠,暂停执行millis毫秒
 30 				sleep(1000); //1000毫秒,即1s
 31 			} catch (InterruptedException e) {
 32 				//重写的方法不能抛出比被重写的方法不同的异常
 33 				//此处只能写try catch,不能写throws
 34 				return;
 35 			}
 36 		}
 37 	}
 38 }

(3)线程合并

  1 public class Main {
  2   public static void main(String[] args) {
  3     MyThread2 t1 = new MyThread2("myThread");
  4     t1.start();
  5     try {
  6     	t1.join();
  7     } catch (InterruptedException e) {}
  8 
  9     for(int i=1;i<=3;i++){
 10       System.out.println("i am main thread");
 11     }
 12   }
 13 }
 14 class MyThread2 extends Thread {
 15   MyThread2(String s){
 16   	super(s);//设置当前线程名为 s
 17   }
 18 
 19   public void run(){
 20     for(int i =1;i<=3;i++){
 21       System.out.println("i am "+getName());//输出线程名
 22     }
 23   }
 24 }//线程合并结果,相当于方法调用

三、线程同步与死锁

1、线程同步

      Java引入了对象互斥锁的概念,使用synchronized修饰符修饰方法和代码块,表明在某一时间段内,只能有一个线程访问被锁住的同步对象或同步方法,以保证共享数据的完整性;但其他线程仍可以访问没有锁定的方法,所以,要想保证数据同步,需将所有改变该属性值的方法都加锁,但锁加的越多,执行效率会被降低。建议对于读属性的方法无需加锁  。

  1 public class Main implements Runnable {
  2 
  3 	private static int num = 100;
  4 
  5 	public static void main(String[] args) throws Exception {
  6 		Main test = new Main();
  7 	    Thread t1 = new Thread(test);
  8 	    Thread t2 = new Thread(test);
  9 	    //设置线程名字
 10 	    t1.setName("t1");
 11 	    t2.setName("t2");
 12 	    //启动线程
 13 	    t1.start();
 14 	    t2.start();
 15 
 16 	   // test.m2();//使t1和t2同步
 17 	   // test.m3();//使t1、t2和主线程 同步	   
 18 	   // System.out.println("Main"+" : "+(num));
 19 	}
 20 	 //在某一时间段,保证只有一个线程访问被锁住的方法
 21 	public synchronized void m1(){
 22 		//若不使用死锁,num++和num输出 的原子性过程可能会打断,结果是都是102或101
 23 		num++;
 24 		System.out.println(Thread.currentThread().getName() +": "+ num);
 25 	}
 26 
 27 	//其他线程仍可以访问没有锁定的方法,导致数据有可能不同步
 28 	public  void m2() {
 29 		num++;
 30 	}
 31 
 32 	//要想保证数据同步,需将所有改变该属性值的方法都加锁
 33 	public  void m3() {
 34 		synchronized (this) {//互斥锁
 35 			num++;
 36 		}
 37 	  }
 38 
 39 	public void run() {
 40 		try {
 41 			m1();
 42 		} catch(Exception e) {
 43 			e.printStackTrace();
 44 		}
 45 	}
 46 }

2、线程死锁

(1)死锁实例

  1 public class Main  implements Runnable {
  2 	public int flag = 1;
  3 	static Object o1 = new Object(), o2 = new Object();
  4 
  5 	public static void main(String[] args) {
  6 		Main td1 = new Main();
  7 		Main td2 = new Main();
  8 		td1.flag = 1;
  9 		td2.flag = 0;
 10 		Thread t1 = new Thread(td1);
 11 		Thread t2 = new Thread(td2);
 12 		t1.start();
 13 		t2.start();
 14 	}
 15 
 16 	public void run() {
 17 		System.out.println("flag=" + flag);
 18 		if(flag == 1) {
 19 			synchronized(o1) {
 20 				try {
 21 					Thread.sleep(500);
 22 				} catch (Exception e) {
 23 					e.printStackTrace();
 24 				}
 25 				synchronized(o2) {
 26 					System.out.println("1");
 27 				}
 28 			}
 29 		}
 30 		if(flag == 0) {
 31 			synchronized(o2) {
 32 				try {
 33 					Thread.sleep(500);
 34 				} catch (Exception e) {
 35 					e.printStackTrace();
 36 				}
 37 				synchronized(o1) {
 38 					System.out.println("0");
 39 				}
 40 			}
 41 		}
 42 	}
 43 }

四、经典同步问题

1、生产者消费者

  1 
  2 public class Main {
  3 	public static void main(String[] args) {
  4 		SyncStack ss = new SyncStack();
  5 
  6 		//生成者
  7 		Producer p = new Producer(ss);
  8 		new Thread(p).start();
  9 
 10 		//消费者
 11 		Consumer c = new Consumer(ss);
 12 		new Thread(c).start();
 13 	}
 14 }
 15 
 16 class WoTou {
 17 	int id;
 18 	WoTou(int id) {
 19 		this.id = id;
 20 	}
 21 	public String toString() {
 22 		return "WoTou : " + id;
 23 	}
 24 }
 25 
 26 //支持多线程同步操作的堆栈的实现
 27 class SyncStack {
 28 	private int index = 0;
 29 	private WoTou[] arrWT = new WoTou[6];
 30 
 31 	public synchronized void push(WoTou wt) {
 32 		while(index == arrWT.length) {
 33 			try {
 34 				//Object的wait只能在synchronized修饰的方法中使用
 35 				//让当前正在访问的线程wait
 36 				this.wait();
 37 			} catch (InterruptedException e) {
 38 				e.printStackTrace();
 39 			}
 40 		}
 41 		this.notifyAll();//唤醒所有线程,此处也可使用notify()唤醒一个线程;
 42 
 43 		arrWT[index] = wt;
 44 		index ++;
 45 	}
 46 
 47 	public synchronized WoTou pop() {
 48 		//不能写if,在发生异常后还需在判断index值
 49 		while(index == 0) {
 50 			try {
 51 				this.wait();
 52 			} catch (InterruptedException e) {
 53 				e.printStackTrace();
 54 			}
 55 		}
 56 		this.notifyAll();//	notify();
 57 		index--;
 58 		return arrWT[index];
 59 	}
 60 }
 61 
 62 class Producer implements Runnable {
 63 	SyncStack ss = null;
 64 	Producer(SyncStack ss) {
 65 		this.ss = ss;
 66 	}
 67 
 68 	public void run() {
 69 		for(int i=0; i<20; i++) {
 70 			WoTou wt = new WoTou(i);
 71 			ss.push(wt);
 72 			System.out.println("生产了:" + wt);
 73 
 74 			try {
 75 				Thread.sleep((int)(Math.random() * 200));
 76 			} catch (InterruptedException e) {
 77 				e.printStackTrace();
 78 			}
 79 		}
 80 	}
 81 }
 82 
 83 class Consumer implements Runnable {
 84 	SyncStack ss = null;
 85 	Consumer(SyncStack ss) {
 86 		this.ss = ss;
 87 	}
 88 
 89 	public void run() {
 90 		for(int i=0; i<20; i++) {
 91 			WoTou wt = ss.pop();
 92 			System.out.println("消费了: " + wt);
 93 			try {
 94 				Thread.sleep((int)(Math.random() * 1000));
 95 			} catch (InterruptedException e) {
 96 				e.printStackTrace();
 97 			}
 98 		}
 99 	}
100 }


2、银行家算法

posted @ 2018-01-17 12:07  苏贺  阅读(266)  评论(0编辑  收藏  举报