什么是线程:线程是比进程还要小的运行单位,一个进程包含多个线程。 比如说一个程序是由很多行代码组成的,那么代码可以分成很多块放到线程当中去分别执行,可以认为线程看作一个子程序。
CPU时间片的轮转:
线程的创建:
通过继承Thread类实现线程:
两个线程同时启动:
总结:线程获得CPU的使用权都是随机的。
通过实现Runnable 线程:
Runnable中的run()方法可以被多个线程共享,可以用于多个线程处理同一个资源的情况。下图中 变量i相当于资源,t1和t2都共享了这个资源。
线程的状态和生命周期:
sleep方法应用
案例:
join方法应用:
调用join方法的线程执行结束之后,其他线程才能执行。过了等待终止时间之后,不管该线程有没有执行完毕,其他线程都可以执行。
案例:
线程优先级:
优先级常量:
优先级相关的方法:
案例:
多线程运行的问题:
案例:
Bank.java
package com.imooc.bank;
public class Bank {
private String account;// 账号
private int balance;// 账户余额
public Bank(String account, int balance) {
this.account = account;
this.balance = balance;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public String toString() {
return "Bank [账号:" + account + ", 余额:" + balance + "]";
}
// 存款
public void saveAccount() {
// 获取当前的账号余额
int balance = getBalance();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 修改余额,存100元
balance += 100;
// 修改账户余额
setBalance(balance);
// 输出存款后的账户余额
System.out.println("存款后的账户余额为:" + balance);
}
public void drawAccount() {
// 在不同的位置处添加sleep方法
// 获得当前的帐户余额
int balance = getBalance();
// 修改余额,取200
balance = balance - 200;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 修改帐户余额
setBalance(balance);
System.out.println("取款后的帐户余额:" + balance);
}
}
SaveAccount.java
package com.imooc.bank;
//存款
public class SaveAccount implements Runnable{
Bank bank;
public SaveAccount(Bank bank){
this.bank=bank;
}
public void run(){
bank.saveAccount();
}
}
DrawAccount.java
package com.imooc.bank;
//取款
public class DrawAccount implements Runnable{
Bank bank;
public DrawAccount(Bank bank){
this.bank=bank;
}
@Override
public void run() {
bank.drawAccount();
}
}
Test.java
package com.imooc.bank;
public class Test {
public static void main(String[] args) {
// 创建帐户,给定余额为1000
Bank bank=new Bank("1001",1000);
//创建线程对象
SaveAccount sa=new SaveAccount(bank);
DrawAccount da=new DrawAccount(bank);
Thread save=new Thread(sa);
Thread draw=new Thread(da);
save.start();
draw.start();
try {
draw.join();
save.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(bank);
}
}
运行结果:
注意:两个线程线程同时运行期间,比如在进行存款的时候,正好运行到
balance += 100;
然后存款线程随时可能停止,然后被取款线程取得了CPU使用权,开始了运行取款的代码,然而存款操作只执行到一半尚未完成存款操作,又开始了取款操作改变了账号的金额,这样子 数据便会混乱 发生脏读等现象。
解决方法:
使用了synchronized的方法 那么在这个方法执行完之前,其他线程是不允许将其打断的。
实现方式案例:
线程间通信问题:案例:
Consumer.java
package com.imooc.queue;
public class Consumer implements Runnable{
Queue queue;
Consumer(Queue queue){
this.queue=queue;
}
@Override
public void run() {
while(true){
queue.get();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Producer.java
package com.imooc.queue;
public class Producer implements Runnable{
Queue queue;
Producer(Queue queue){
this.queue=queue;
}
@Override
public void run() {
int i=0;
while(true){
queue.set(i++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Queue.java
package com.imooc.queue;
public class Queue {
private int n;
public synchronized int get() {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("消费:"+n);
return n;
}
public synchronized void set(int n) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("生产:"+n);
this.n = n;
}
}
运行结果:
运行会出现上图的情况,并没有我们想要的 生产一个,便消费一个的效果。
可以运用下图的方法来解决 红框为增加的代码
比如 当flag为flase的时候 即当容器当中没有数据的时候 是不能进行消费的 get方法需要进入等待状态。
当flag为ture的时候是不能进行生产的 set方法则进入等待状态。
当线程进入等待的时候,不能全部线程都进入等待,不然会造成死锁。这时在结束之前唤醒所有线程。之所以唤醒所有线程是因为在线程多的情况下不确定哪条线程是需要执行的,全部唤醒通过flag值来决定执行哪个线程。
notifyAll();//唤醒所有处于等待的线程,使他们结束等待。
总结:
使用第二种方法比较多,因为JAVA不允许多继承。