基本概念
基本:
- 程序:一段静态代码,静态对象
- 进程:程序的一次执行过程
- 线程:进程内部一个执行路径
比如:QQ正在运行是一个进程,同时聊天,视频 两个线程
何时需要多线程:
- 程序需要执行多个任务
- 执行等待的任务:如搜索时需要等待服务器返回响应,等待时间让CPU做别的事情,先让这个程序占有的CPU做别的事情
- 后台运行程序:在主程序的执行过程中,中途有一行代码开启了线程,当其中的主程序运行的代码和线程代码并行执行,则主程序代码和线程代码就不想关了
多线程的建立与启动
thread类(1)的:
- 特性run() :线程体:代码写在run()方法中
- start():用来启动线程
- 异步性:
- 两个线程各自保持前进的速度,你执行一次,我执行一次,接着我执行或者你执行,按照不同的顺序推进
创建线程的两种方式:
继承Thread类:
-
创建一个子类继承thread类
-
子类重写run方法
-
创建子类对象:即创建了线程对象
-
调用对象的start方法
public class Test {
public static void main(String[] args) {
//3步:创建多线程对象
Thread t=new ThreadTest();
t.start();
//4步:调用start方法,启动线程,调用run方法
//threadTest.start();System.out.println("=========="); System.out.println("=======5==="); System.out.println("==========");
}
}/**创建一个子类继承thread类
- 多线程:继承thread创建多线程
- @author douer
*/
public class ThreadTest extends Thread{
//1.重写thread
@Override
public void run() {
System.out.println(“多线程运行代码”);
for(int i=0;i<3;i++) {
System.out.println(i+6);
System.out.println(i+6);
}
}
}
实现接口Runnable:
-
定义子类实现runnable接口
子类重写run方法
public class RunableTest implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" : 多线程运行代码"); -
在test中通过thread类含餐构造创建线程方法
Thread t2=new Thread(new RunableTest(), “t-2 :”);//参数1是new一个实例,参数2是线程的名字 -
start开启线程
-
Thread.currentThread().getName():获取当前线程的名称
-
线程名称的作用:多线程运行时,可以明确是具体哪个的线程在运行
实现接口:
package ThreadTest;
/**
* 实现runnable接口的方式创建多线程
* @author douer
*
*/
public class RunableTest implements Runnable {@Override public void run() { System.out.println(Thread.currentThread().getName()+" : 多线程运行代码"); for(int i=0;i<3;i++) { System.out.println(Thread.currentThread().getName()+"==="+i); } } }
Test运行:
package ThreadTest;public class Test { public static void main(String[] args) { //第二种Runnable方 //2步:通过thread Thread t2=new Thread(new RunableTest(), "t-2 :");//参数1是new一个实例,参数2是线程的名字 t2.start(); //作用:多线程运行时,可以明确是具体哪个的线程在运行 Thread t3=new Thread(new RunableTest(), "t-3 :"); t3.start(); System.out.println("=======1=="); System.out.println("=======5==="); System.out.println("=======7=="); } }
两者联系and区别:
-
区别:
-
常用接口方式来实现多线程:好处:
- 避免单继承的局限:‘
子类可以实现多个接口,但是继承只能继承一个。比如student同时实现多线程,同时,又实现person的接口 - 共享同一个接口的对象==从而共同处理一份资源
ex:-
两个线程共同增加count 数值
-
两个线程共享同一个对象
/new 同一个对象
RunableTest r=new RunableTest();
//参数r,被两个线程共享
Thread t2=new Thread(r, “t-2 :”);
t2.start();
//作用:多线程运行时,可以明确是具体哪个的线程在运行
Thread t3=new Thread(r, “t-3 :”);
t3.start();
两个线程同时对count进行++
public class RunableTest implements Runnable {int count=0; @Override public void run() { //System.out.println(Thread.currentThread().getName()+" : 多线程运行代码"+count); for(int i=0;i<3;i++) { //System.out.println(Thread.currentThread().getName()+"==="+i); count++; System.out.println(Thread.currentThread().getName()+" : 多线程运行代码"+count); } }
}
-
- 避免单继承的局限:‘
多线程优点
- 提高应用程序的响应
- CPU的利用率
- 改善程序结构:一个程序可以分成多个线程并行运行
thread类(2)的方法
-
join()阻塞:
专业说法:阻塞当前的main方法,先不执行System.out.println("=====5=");
先执行jion进来的代码,jion的线程执行全部完毕之后继续执行之前main阻塞的代码
RunableTest r=new RunableTest();
Thread t2=new Thread(r, “t-2 :”);
t2.start();
Thread t3=new Thread(r, “t-3 :”);
t3.start();System.out.println("=======1=="); //把t2的run()方法添加到main中执行, //执行顺序一定是t2中的run一定在1与5之间 t2.join(); System.out.println("=======5==="); System.out.println("=======7==");
-
yield():线程让步
暂停现在执行的程序, -
sleep():睡眠
Thread.sleep(1000);
在实现线程的run中调用; 单位是毫秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} -
stop():强制停止线程声明周期
RunableTest r=new RunableTest();
Thread t2=new Thread(r, “t-2 :”);
t2.start();
Thread t3=new Thread(r, “t-3 :”);
t3.start();System.out.println("=======1=="); System.out.println(t2.isAlive());//确认还活着 t2.stop();//在这句话之前t-2可以执行,之后就不能执行 System.out.println("=======5==="); t3.join(); System.out.println("=======7==");
t-2只执行了一次就消失了
-
boolean isAlive():判断当前线程是否还存活
线程生命周期
- 新建:创建线程实例
- 就绪:执行了start()方法之后,进入队列等待CPU的时间片
- 运行:run()方法开始执行
- 阻塞:run()被卡住
- 死亡:强制死亡stop();自然死亡;断电;杀掉进程
线程死锁
- 成因:不同的线程占用需要的同步资源不放手,相互等待对方释放自己需要的同步资源
- 解决方法:合理安排执行顺序;如:加锁顺序一致
线程通信
-
wait():挂起线程==阻塞
-
notify():唤醒排队中的最高级线程
-
notifyAll():唤醒所有线程
//案例:实现:支付宝永远先执行,如果微信想先执行,则阻塞他
使用this:
public synchronized void drawing8(int m) throws Exception {
String name=Thread.currentThread().getName();if(name.equals("微信")) { this.wait(); //当前对象阻塞 } //取钱数目超过余额 if(money<m) { System.out.println(name+"操作:账户金额不足,余额为:"+money); }else { System.out.println(name+"操作:账户原有金额:"+money); money-=m; System.out.println(name+"操作:账户提款金额:"+m); System.out.println(name+"操作:账户提款之后金额:"+money); } if(name.equals("支付宝")) { this.notify(); //唤醒当前最高优先级线程,处于就绪状态 } }
生产者消费者问题
P V原理的实现
package ThreadTest;
/**
* 生产者消费者:
*
* @author douer
*
*/
/*
* 1.生产者生产的时候不消费,消费时不是生产
* 2. 产品数为0开始生产,产品>5则停止生产
* 3.产品数==5则开始消费,直到全部消费完成
*/
public class Test4 {
public static void main(String[] args) {
Clerk c=new Clerk();
//生产者
//创建一个线程
//使用匿名类创建一个线程
new Thread(new Runnable() {
@Override
public void run() {
//加锁
synchronized(c) {
//无限循环:跳出条件在里面
while(true) {//考试时以为这个没用呢
//产品数为0开始生产;直到到达一定数额
if(c.productNum==0) {
System.out.println("=====开始生产:"+c.productNum);
while(c.productNum<4) {
System.out.println("目前库存:"+c.productNum++);
}
//生产完毕则跳出循环
System.out.println("====生产完毕::::"+c.productNum);
//生产完毕之后唤醒消费进程
c.notify();
}
//产品还有,不生产,进程阻塞
else {
try {
c.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}, "生产者").start();
//消费者
new Thread(new Runnable() {
@Override
public void run() {
//加锁
synchronized(c) {
//无限循环:跳出条件在里面
while(true) {
//产品数为0开始生产;直到到达一定数额
if(c.productNum==4) {
System.out.println("=====开始消费哦:"+c.productNum);
while(c.productNum>0) {
System.out.println("目前库存:"+c.productNum--);
}
//生产完毕则跳出循环
System.out.println("====消费完毕::::"+c.productNum);
c.notify();
}
//产品还有,不生产,进程阻塞
else {
try {
c.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}, "生产者").start();
//
// producer.start();
// consum.start();
}
}
class Clerk{
public static int productNum=0;
}