P580~P599
程序
为完成特定任务、用某种语言编写的一组指令的集合。
进程
进程是运行中的程序,是程序的一次执行过程,或是正在运行的一个程序。是动态过程,有它自身的产生、存在和消亡的过程
线程
- 线程由进程创建,是进程的一个实体
- 一个进程可以有多个线程
单线程
同一时刻只允许执行一个线程
多线程
同一时刻可以执行多个线程
并发
同一时刻,多个任务交替执行,造成一种“貌似同时”的错觉。简单的说,单核CPU实现的多任务就是并发
并行
同一时刻,多个任务同时执行。多核CPU可以实现并行
线程基本使用
java中线程使用有两种方法
- 继承Thread类,重写run方法,Thread01.java
- 编写程序,开启一个线程,每隔一秒,在控制台输出“喵喵”
- 当输出80次时,结束该线程
- 使用JConsole监控线程执行情况
public class Thread01 {
public static void main(String[] args) throws InterruptedException {
Cat15 cat = new Cat15();
cat.start();
System.out.println("主线程"+Thread.currentThread().getName());
for (int i = 0; i < 10; i++) {
System.out.println("主线程"+i);
Thread.sleep(1000);
}
}
}
//一个类继承Thread类,该类就可以当作线程使用
//重写run方法,写自己业务逻辑
//run Thread类实现了Runnable接口的run方法
class Cat15 extends Thread{
int times = 0;
@Override
public void run() {//
int times =0;
do {
System.out.println("miaomiao");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
times++;
} while (times != 10);
}
}
- 实现Runnable接口,重写run方法,Thread02.java
- java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时在用继承Thread类方法来创建线程显然不可能
- java设计者们提供了另一个方式创建线程,就是通过实现Runnable接口来创建线程。底层使用了设计模式【代理模式】
- 实现Runnable接口方式更适合多个线程共享一个资源的情况,并且避免了单继承的限制
public class Thread02 {
public static void main(String[] args) {
Dog15 dog = new Dog15();
//创建Thread对象,把dog对象 ,放入Thread
Thread thread = new Thread(dog);
thread.start();
}
}
class Dog15 implements Runnable{
int count=0;
@Override
public void run() {
while(true){
System.out.println("wang"+(++count)+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count==10){
break;
}
}
}
}
- 多线程,Thread03.java
public class Thread03 {
public static void main(String[] args) {
T1 t1 = new T1();
T2 t2 = new T2();
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
thread1.start();
thread2.start();
}
}
class T1 implements Runnable{
int count=0;
@Override
public void run() {
while(true){
System.out.println("hello1"+(++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count==10){
break;
}
}
}
}
class T2 implements Runnable{
int count=0;
@Override
public void run() {
while(true){
System.out.println("hello2"+(++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count==10){
break;
}
}
}
}
线程终止
- 当线程完成任务后,会自动退出
- 可以通过使用变量来控制run方法退出的方式停止线程
线程常用方法
- setName,设置线程名称
- getName,获取线程名称
- start,线程开始执行,JVM底层调用该线程的start0方法
- run,调用线程run方法
- setPriority,设置线程优先级
- getPriority,获取线程优先级
- sleep,指定毫秒数让当前线程休眠
- interrupt,中断线程的休眠
- yield,线程的礼让,让出cpu,让其他线程执行,但礼让的时间不确定,所以不一定礼让成功
- join,线程的插队,插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务
用户线程和守护线程
-
用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
-
守护线程:一般是为用户线程服务,当所有用户线程结束,守护线程自动结束
希望main结束后,子线程自动结束,设置子线程为守护线程即可
myDaemonThread.setDaemon(true);
-
常见的守护线程:垃圾回收机制
线程的生命周期
- NEW
- RUNNABLE
- BLOCKED
- WAITTING
- TIMED_WAITTING
- TERMINATED
线程的同步
- 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性
- 也可理解:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作
同步方法,Synchronized
synchronized(对象){//同步代码块,对象:this,静态方法中:类名.class
//需要同步的代码
}
public synchronized void m(String name){//同步方法
//需要同步的代码
}
互斥锁
- Java语言中,引入对象互斥锁的概念,来保证共享数据操作的完整性
- 每个对象都对应于一个称为互斥锁的标记,这个标记用来保证在任意时刻,只能有一个线程访问该对象
- 关键字synchronized来与对象的互斥锁联系,当某个对象用synchronized修饰时,表明该对象在任意时刻只能由一个线程访问
- 同步的局限性:导致程序的执行效率要降低
- 非静态同步方法的锁可以是this,也可以是其他对象(要求是同一个对象)
- 静态同步方法的锁为当前类本身
互斥锁注意事项
- 同步方法如果没有使用static修饰,默认锁对象:this
- 如果方法使用static修饰,默认锁对象:当前类.class
- 实现的落地步骤:
- 需要先分析上锁代码
- 选择同步代码块或同步方法
- 要求多个线程的锁对象为同一个即可
线程死锁
多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,要避免死锁的发生
释放锁
以下操作会释放锁
- 当前线程的同步方法、同步代码块执行结束
- 当前线程在同步代码块、同步方法中遇到break、return
- 当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束
- 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁
以下操作不会释放锁
- 线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁
- 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁
本章作业,Homework1501.java
- 在main方法中启动两个线程
- 第一个线程循环随机打印100以内数
- 直到第二个线程从键盘读取了Q
public class Homework1501 {
public static void main(String[] args) {
Th1 th1 = new Th1();
Th2 th2 = new Th2(th1);
Thread thread1 = new Thread(th1);
Thread thread2 = new Thread(th2);
thread1.start();
thread2.start();
}
}
class Th1 implements Runnable{
private boolean loop =true;
@Override
public void run() {
while(loop){
System.out.println((int)(Math.random()*100+1));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public boolean isLoop() {
return loop;
}
public void setLoop(boolean loop) {
this.loop = loop;
}
}
class Th2 implements Runnable {
private Th1 th1;
private Scanner scanner = new Scanner(System.in);
public Th2(Th1 th1) {//构造器中直接传Th1
this.th1 = th1;
}
@Override
public void run() {
while (true) {
System.out.println("请输入");
char key = scanner.next().toUpperCase().charAt(0);
if(key=='Q'){
//以通知方式结束Th1
th1.setLoop(false);
break;
}
}
}
}
Homework1502.java
- 有两个用户分别从同一个卡上取钱(总额10000)
- 每次取出1000,卡余额不足时,不能取款
- 不能超取
public class Homework1502 {
public static void main(String[] args) {
T3 a = new T3();
Thread thread1 = new Thread(a);
Thread thread2 = new Thread(a);
thread1.start();
thread2.start();
}
}
class T3 implements Runnable {
static double money = 10000;
@Override
public void run() {
while (true) {
//synchronized实现线程同步
//多个线程执行到这里时,就会去争夺this对象
//谁争夺到this对象锁,就执行synchronized代码块,执行完后,就释放this对象锁
//争夺不到this对象锁,就blocked,准备继续争夺
synchronized (this) {
if (money <= 1000) {
System.out.println("余额不足");
break;
}
money -= 1000;
System.out.println("余额" + money);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}