一、线程具有优先级(priority)
线程的优先级分为十个等级,分别从1到10,优先级的高低决定了线程被CPU执行的先后顺序。(但不是绝对的,只是优先级越高,抢占CPU的几率就越大)
Thread类有三个关于线程优先级的静态变量:MAX_PRIORITY表示最大优先级,为10;MIN_PRIORITY表示最先优先级,为1;NORM_PRIORITY表示普通优先级,为5.
二、线程的调度
线程的调度有两种模型:分时模型和抢占模型
分时模型:线程在指定的时间里执行(在CPU上)
抢占模型:线程一旦获得执行权,就一直执行下去。(Java语言支持的是抢占模型)
(重点)三、Java的Thread线程类和Runnable接口
创造线程有两种方法,一种是继承Thread类,一种是实现Runnable接口
Thread类是线程类,利用Thread的子类创造线程
Runnable是接口,只有一个run()方法。相当于重写了Thread类的run()方法。但实现Runnable接口的类必须作为另一个Thread类对象的参数,以此来创造线程。
继承Thread类创造线程 1
1 class MyThread extends Thread{ //继承Thread类
2 private String who;
3 public MyThread(String str) {
4 who=str; //初始化who
5 }
6 //覆盖Thread的run方法,线程的开始也是从run开始的
7 public void run() {
8 System.out.println(who+"最先运行");
9 }
10 }
11 public class UseThread {
12 public static void main(String[] args) {
13 MyThread you=new MyThread("你");
14 MyThread she=new MyThread("她");
15 she.setPriority(1); //设置she的优先级
16 you.start(); //你 线程开始
17 she.start(); //她 线程开始
18 //注意是start()
19 System.out.println("you的优先级为"+you.getPriority());
20 System.out.println("she的优先级为"+she.getPriority());
21 System.out.println("main结束");
22 }
23 }
实现Runnable接口创造线程
1 class MyRun implements Runnable{
2 private String who;
3 public MyRun(String str) {
4 who=str;
5 }
6 @Override
7 public void run() {
8 for(int i=0;i<5;i++) {
9 try {
10 //因为是实现接口所以需要加前缀Thread
11 Thread.sleep((int)(1000*Math.random()));
12 } catch (Exception e) {
13 System.out.println(e.toString());
14 }
15 System.out.println(who+"正在运行!!");
16 }
17 }
18 }
19 public class UseRunnable {
20 public static void main(String[] args) {
21 /*
22 MyRun you=new MyRun("你");
23 MyRun she=new MyRun("她");
24 //将实现接口的对象床底给Thread并生成Thread对象
25 Thread t1=new Thread(you);
26 Thread t2=new Thread(she);
27 t1.start();
28 t2.start();
29 System.out.println("main运行结束");
30 */
31 //让you和she逐个运行
32 MyRun you=new MyRun("你");
33 MyRun she=new MyRun("她");
34 //将实现接口的对象床底给Thread并生成Thread对象
35 Thread t1=new Thread(you);
36 Thread t2=new Thread(she);
37 t1.start();
38 try {
39 t1.join(); //使用join()时,需让此时的线程先执行完,下一个线程才能执行
40 } catch (InterruptedException e) {
41 System.out.println(e.toString());
42 }
43 t2.start();
44 try {
45 t2.join();
46 } catch (InterruptedException e) {
47 System.out.println(e.toString());
48 }
49 System.out.println("main运行结束");
50 }
51 }
(main方法也是一个线程)
四、关于线程的数据共享
如果线程的操作是共享某个数据,一般都是用实现Runnable接口的类,把数据放在Runnable接口的类中,然后再创造线程。
比如三个线程在售票
1 class ThreadShare implements Runnable{
2 private int tickets=10;
3 @Override
4 public void run() {
5 while(true) {
6 if(tickets>0) {
7 System.out.println(Thread.currentThread().getName()+"售机票第"+tickets--+"号");
8 }
9 else
10 System.exit(0);
11 }
12 }
13
14 }
15 public class ThreadShareMain {
16 public static void main(String[] args) {
17 ThreadShare t=new ThreadShare();
18 //用此对象t作为参数创建三个线程
19 Thread t1=new Thread(t,"第1售票窗口");
20 Thread t2=new Thread(t,"第2售票窗口");
21 Thread t3=new Thread(t,"第3售票窗口");
22 t1.start();
23 t2.start();
24 t3.start();
25 }
26 }
五、关于同步线程
线程在一个完整操作的所有动作的执行过程中,都占有相关资源而不被打断。
临界资源:在一个时刻只能被一个线程访问的资源
临界代码:访问临界资源的那段代码
六、“互斥锁”机制
Java每个对象都有一个“互斥锁”与之连接。一个对象只有一个互斥锁。一个线程只有获得互斥锁后,对其进行操作,而另一个线程只能处于等待状态。所以利用对一个对象互斥锁的争夺,可以实现不同线程的互斥效果。
七、synchronized关键字标识同步的资源
格式一:同步语句
Synchronized(对象){
临界代码段
}
格式二:同步方法
public synchronized 返回类型 方法名(){
方法体
}
synchronized的功能是先判断对象或方法的互斥锁是否存在,若存在就获得互斥锁,然后执行紧随其后的临界代码或方法体;如果对象的互斥锁不存在(已被其他线程拿走),就进入等待状态,直到获得互斥锁。
synchronized很好的解决像银行业务取款并发的情况
1 //模拟银行账户类
2 class Mbank{
3 private static int sum=2000;
4 //修饰线程同步的方法
5 public synchronized static void take(int k) {
6 int temp=sum;
7 temp-=k;
8 try {
9 Thread.sleep((int)(1000*Math.random()));
10 } catch (InterruptedException e) {
11 e.toString();
12 }
13 sum=temp;
14 System.out.println(Thread.currentThread().getName()+"sum="+sum);
15 }
16 }
17 //模拟用户取款
18 class Customer extends Thread{
19 public void run() {
20 for(int i=0;i<4;i++) {
21 Mbank.take(100); // 用户每次取出100,取四次
22 }
23 }
24 }
25
26 //调用线程的主类
27 public class SynchronizedThread {
28 public static void main(String[] args) {
29 Customer c1=new Customer();
30 Customer c2=new Customer();
31 c1.start();
32 c2.start();
33 }
34 }
八、线程之间的通信
一个线程的执行,需要另一个线程配合,就称作线程的通信
例如:售票操作只有当有存票才能进行售票,不然只有当存票操作后才能进行售票。
线程的通信需要用到java.lang.Object类的三个方法:
wait():线程A执行了对象x的同步代码(synchronized)里的wait()方法,该线程暂停进行,然后进入对象x的等待队列,并释放对象x的互斥锁。直到其他线程在对象x上调用notify()或notifyAll()方法,才能够重新获得对象x的互斥锁。
notify():唤醒正在等待该对象互斥锁的第一个线程
notifyAll():唤醒正在等待该对象互斥锁的所有线程,具有最高优先级的线程首先被唤醒
(wait方法必须写在try-catch里面)
写一个简单的存票售票程序:
1 //模拟存票与售票
2 class Tickets{
3 protected int size;//总票数
4 int number=0; //票号
5 boolean available=false; //表示当前是否有票可售
6 public Tickets(int n) {
7 this.size=n;
8 }
9 /*
10 * 同步方法,实现存票
11 */
12 public synchronized void put() {
13 if(available) { //如果还有剩余票就等待
14 try {
15 wait();
16 } catch (Exception e) { }
17 }
18 System.out.println("存入第【"+(++number)+"】号票");
19 available=true;
20 notify(); //存票后唤醒售票线程开始售票
21 }
22 /*
23 * 同步方法,实现售票
24 */
25 public synchronized void sell() {
26 if(!available) { //如果没有票,则售票线程等待
27 try {
28 wait();
29 } catch (Exception e) { }
30 }
31 System.out.println("售出第【"+(number)+"】号票");
32 available=false;
33 notify(); //售票后唤醒存票线程开始存票
34 if(number==size)number=size+1; //在售完最后一张票后,设置一个结束标志
35 //number>size表示售票结束
36 }
37 }
38 //存票线程类
39 class Producer extends Thread{
40 Tickets t=null;
41 public Producer(Tickets t) {
42 this.t=t;
43 }
44 public void run() {
45 while(t.number<t.size) {
46 t.put();
47 }
48 }
49 }
50 class Consumer extends Thread{
51 Tickets t=null;
52 public Consumer(Tickets t) {
53 this.t=t;
54 }
55 public void run() {
56 while(t.number<=t.size) {
57 t.sell();
58 }
59 }
60 }
61 public class ThreadNotify {
62 public static void main(String[] args) {
63 Tickets t=new Tickets(10);
64 new Producer(t).start(); //存票线程开始
65 new Consumer(t).start(); //售票线程开始
66 }
67 }