(一)概念
1.程序:是指包含指令和数据文件的集合。
2.进程:是程序一次执行过程,因此进程是动态的。
3.多任务:一个系统同时执行多个进程。比如在Win10同时运行QQ、WeChat。
4.线程:是进程的一部分,比如一个进程可以有多个线程。
5.多线程:同时执行一个以上的线程,线程可以同时发生在同一时刻,一个线程不必等待另一个线程执行完毕。
(二).线程的生命周期
(三).线程调度
1.线程调度的目的:对处于可运行状态的多个线程对象进行系统级的协调,防止多个线程争用有限资源而导致系统死机或者崩溃。
2.调度策略原理:java 的调度策略是基于线程优先级的抢先式调度。意思就是,谁的优先级高那我就先给谁使用系统资源。比如说,当前正在运行一个优先级为5的线程,这个时候来了一个优先级为8的线程,那么系统就会把当前运行的线程挂起,去运行这个优先级高的线程,一直等到这个优先级高的线程运行完毕才会继续运行它。如果说来的也是一个优先级为5的线程,那么他们两个就是轮流使用系统资源,谁也不用让谁。
3.线程的控制:当然了,在java中也可以人为的去干预线程的运行规则,比如说希望明确的让一个线程给另一个线程运行的机会,那么可以采取下列4种办法:
- 调整线程各个的优先级
- 让处于运行中的线程调用sleep()睡一会,从而让出系统CPU资源
- 让运行中的线程调用yield()方法,实现主动把CPU资源交给下一个可运行的线程
- 让处于运行的线程调用另一个线程的join()方法,等待另一个线程的结束
4.线程的优先级
所有的线程都又一个优先级,然后所有处于可运行状态的线程都是根据自身的优先级排列在可运行队列中的。优先级高的获得较多的运行机会,在java中优先级分为了10个等级,依次用整数1~10表示。在Thread类里面封装了三个静态的常量
MAX_PRORITY 的值是10 ,表示最高级
MIN_PRORITY 的值是1,表示最低级
NORM_PRORITY 的值是5,在java中的线程默认级别
每一个线程的默认优先级都继承自它的父线程,如果要想获得线程的优先级可以使用getPriority()方法,设置线程的优先级使用setPriority().
(四)代码演示
1.多线程抢占CPU资源
package com.ThreadLearn;
public class testThread01 extends Thread {
private String who;
public testThread01(String who) {
this.who=who;
}
public void run() {
for (int i = 0; i < 3; i++) {
try {
Thread.sleep(1000);
System.out.println(who + "正在抢CPU资源");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
testThread01 t1=new testThread01("凯耐");
System.out.println("线程名"+t1.getName()+"\t"+Thread.currentThread());
testThread01 t2=new testThread01("欧丽");
System.out.println("线程名"+t2.getName()+"\t"+Thread.currentThread() );
System.out.println(t2.getName());
t1.start();
t2.start();
System.out.println("是否运行"+t1.isAlive());
System.out.println("是否运行"+t2.isAlive());
System.out.println("主方法main()正在执行...");
}
}
输出:
线程名Thread-0 Thread[main,5,main]
线程名Thread-1 Thread[main,5,main]
Thread-1
是否运行true
是否运行true
主方法main()正在执行...
凯耐正在抢CPU资源
欧丽正在抢CPU资源
欧丽正在抢CPU资源
凯耐正在抢CPU资源
凯耐正在抢CPU资源
欧丽正在抢CPU资源
2.线程优先级
package com.ThreadLearn;
class MyThread extends Thread{
private String message;
public MyThread(String msg){
message = msg;
}
public void run(){
for(int i = 0;i < 3;i++){
System.out.println(message + " " + getPriority());
}
}
}
public class ThreadPriorityDemo{
public static void main(String[] args){
Thread t1 = new MyThread("张三");
t1.setPriority(1);
t1.start();
Thread t2 = new MyThread("李四");
t2.setPriority(5);
t2.start();
Thread t3 = new MyThread("王五");
t3.setPriority(10);
t3.start();
}
}
输出:
李四 5
王五 10
张三 1
王五 10
李四 5
王五 10
张三 1
李四 5
张三 1
注:不要把线程的优先级与运行结果的顺序作为衡量的标准,优先级较高的线程不一定每一次都先执行完run()方法中的任务。
参考文献:http://blog.youkuaiyun.com/facekbook/article/details/52075240
(五)线程间的数据共享
- 共享相同代码: 多个线程执行同一run()方法中的代码。
- 数据共享:多线程共享访问相同对象数据。
(六)代码演示
1.模拟两个售票窗口售6张票。
package ThreadSaleDemo;
public class ThreadSale extends Thread {
private int tickets = 6;
public void run() {
while (true) {
if (tickets > 0) {
System.out.println(getName() + "售高铁票" + tickets-- + "号");
} else {
System.exit(0);
}
}
}
public static void main(String[] args) {
ThreadSale t1 = new ThreadSale();
ThreadSale t2 = new ThreadSale();
t1.start();
t2.start();
}
}
输出:
Thread-0售高铁票6号
Thread-1售高铁票6号
Thread-0售高铁票5号
Thread-1售高铁票5号
Thread-0售高铁票4号
Thread-1售高铁票4号
Thread-1售高铁票3号
Thread-1售高铁票2号
Thread-0售高铁票3号
Thread-1售高铁票1号
Thread-0售高铁票2号
Thread-0售高铁票1号
原因:因为每个线程都有自己的方法和变量,且每个线程都在处理各自的资源。因此模拟售票我们使用Runnable接口。
(七)多线程同步控制
1.多线程同步控制: 指一个线程再处理某一数据时,其他线程是不可以更改此数据。
代码演示:
package ThreadSaleDemo;
public class ChinaBank {
private static int CountAllMoney=1000;
public static void take(int takemoney) {
int temp=CountAllMoney;
temp-=takemoney;
try {
Thread.sleep(1000);
CountAllMoney=temp;
System.out.println("账户余额="+CountAllMoney);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package ThreadSaleDemo;
public class Customer extends Thread {
public void run() {
for(int i=1;i<5;i++) {
ChinaBank.take(200);
}
}
public static void main(String[] args) {
Customer c1=new Customer();
Customer c2=new Customer();
c1.start();
c2.start();
}
}
输出:
账户余额=800
账户余额=800
账户余额=600
账户余额=600
账户余额=400
账户余额=400
账户余额=200
账户余额=200
出错结果:两个并发线程共享同一内存变量所引起。
2.Java利用对象“互斥锁”机制实现线程间的互斥操作。关键字为sychronized标识同步资源。
代码如下:
package ThreadSaleDemo;
public class ChinaBank {
//共享变量必须是private修饰,否则使用synchronized没意义
private static int CountAllMoney=3000;
//锁定临时资源
public synchronized static void take(int takemoney) {
int temp=CountAllMoney;
temp-=takemoney;
try {
Thread.sleep(1000);
CountAllMoney=temp;
System.out.println("账户余额="+CountAllMoney);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package ThreadSaleDemo;
public class Customer extends Thread {
public void run() {
for(int i=1;i<5;i++) {
ChinaBank.take(200);
}
}
public static void main(String[] args) {
Customer c1=new Customer();
Customer c2=new Customer();
c1.start();
c2.start();
}
}
输出 :
账户余额=2800
账户余额=2600
账户余额=2400
账户余额=2200
账户余额=2000
账户余额=1800
账户余额=1600
账户余额=1400
synchronized修饰takemoney()方法,线程c1执行完以后线程c2才能执行
(八)线程之间的通信
1.线程之间的通信目的:多线程的执行有时候需要配合,为了协调不同线程的工作,线程之间需要建立沟通,通过线程之间的对话来解决线程同步问题。
2.线程如何沟通需要:Obeject类中的wait()、notify()方法,且这两方法只能在同步代码块调用。
package ThreadSaleDemo;
class Tickets {
protected int size;
int number=0;
boolean avaiable=false;
public Tickets(int size) {
this.size=size;
}
public synchronized void put() {
if(avaiable)
try {wait();} catch (InterruptedException e) {}
System.out.println("存入第【"+(++number)+"】号票");
avaiable=true;
notify();
}
public synchronized void sell() {
if (!avaiable) {
try {
wait();
} catch (InterruptedException e) {
}
}
System.out.println("售出第【" + (number) + "】号票");
avaiable = false;
notify();
if (number == size) {
number = size + 1;
}
}
}
package ThreadSaleDemo;
public class Producer extends Thread{
Tickets t=null;
public Producer(Tickets t) {
this.t=t;
}
public void run() {
while(t.number<t.size) {
t.put();
}
}
}
package ThreadSaleDemo;
public class Customer extends Thread {
Tickets t=null;
public Customer(Tickets t) {
this.t=t;
}
public void run() {
while(t.number<t.size) {
t.sell();
}
}
}
package ThreadSaleDemo;
public class TestDemo {
public static void main(String[] args) {
Tickets t=new Tickets(5);
new Producer(t).start();
new Customer(t).start();
}
}
输出:
存入第【1】号票
售出第【1】号票
存入第【2】号票
售出第【2】号票
存入第【3】号票
售出第【3】号票
存入第【4】号票
售出第【4】号票
存入第【5】号票
售出第【5】号票
参考:http://blog.youkuaiyun.com/u012834750/article/details/67657240?locationNum=3&fps=1
API文档: http://docs.oracle.com/javase/8/docs/api/index.html