线程与进程
进程:正在运行的程序。
线程:一个进程的执行单元。一条执行流程。
多线程:一个进程中有多条执行路径。
- 进程(Process)
狭义定义:进程是正在运行的程序的实例
广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
多进程可以提高CPU的使用率 - 线程(Thread)
线程是程序中一个单一的顺序控制流程。进程内的执行单元,是CPU的基本单位。 - 多线程
在单个程序中同时运行多个线程完成不同的工作,称为多线程。
作用:
为了提高效率才使用。
什么时候使用?
只有当要操作的代码的内容比较多(耗时),循环次数较多这样的情况才使用。
多线程的生命周期
image.png
多线程的实现方式
Java是不能直接调用系统功能的,所以,我们没办法直接实现多线程程序。
所以Java可以去调用C/C++写好的程序来实现程序的多线程。由C/C++去调用系统功
能创建进程,然后Java去调用,然后提供一些类供我们使用,我们就可以实现多线程了
Java提供的是Thread类,通过API知道其有2种方法实现多线程
(创建新执行线程有两种方法。一种方法是将类声明为 Thread 的子类。
该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例)
Thread类的方法:
void setName(String name)
-- 设置线程的名称
String getName()
-- 获取线程的名称
static Thread currentThread()
-- 获取当前正在运行的线程对象
方式1:继承Thread类的方式
步骤 :
A:自定义类继承Thread类
B:自定义类中重写run()方法
为什么是run()方法呢?
C:创建对象
D:启动线程
注意:
- 为什么是run()方法呢?
不是类中所有的代码都需要被线程执行的。
而这个时候,为了区分那些代码能被线程执行,java提供了Thread类中的run()
用来包含那些被线程执行的代码。 - run()和start()的区别?
- start():首先启动了线程,然后由jvm去调用该线程的run()方法
- run():仅仅是封装线程的方法,直接调用是普通的方法
package com.lianwei.Thread.ExtendsThread;
package com.lianwei.Thread.ExtendsThread;
import com.sun.org.apache.xml.internal.resolver.helpers.PublicId;
public class MyThread extends Thread{
//构造方法起名字
public MyThread(String name) {
super(name);
}
public MyThread() {
}
public void run(){
for (int x=1;x<=10;x++){
System.out.println(getName()+"=>"+x);
}
}
}
public class MyThreadDemo {
public static void main(String[] args){
//获取main方法所在的线程对象的名称,该肿么办?
//public static Thread currentThread():返回当前在线的线程对象
System.out.println(Thread.currentThread().getName());
// 调用方法设置名称
// //创建线程对象
// MyThread mt1 = new MyThread();
// MyThread mt2 = new MyThread();
// //设置线程名称
// mt1.setName("康娜");
// mt2.setName("jyh");
// //启动线程
// mt1.start();
// mt2.start();
// 带参构造方法给线程起名字
MyThread my1 = new MyThread("康娜");
MyThread my2 = new MyThread("jyh");
my1.start();
my2.start();
}
}
方式2:实现Runnable接口的方式
优点:
1)可以避免单继承的局限性。
2)只创建了一个资源对象。更好的实现了数据与操作的分离。
(一般我们选择第二种方式。)
package com.lianwei.Thread.ImpRunable;
import java.lang.Runnable;
public class ImplementsRunnable implements Runnable{
public void run(){
for (int x=0;x<10;x++){
//由于实现接口的方式不能直接使用Thread类的方法了,但可以间接使用
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
package com.lianwei.Thread.ImpRunable;
//步骤:
// A:创建一个类实现Runnable接口
// B:重写run()方法
// C:创建类的对象
// D:把该对象作为Thread类的构造参数传递。创建Thread类的对象,并执行。
public class Runnable {
public static void main(String[] args){
ImplementsRunnable im = new ImplementsRunnable();
// D:把该对象作为Thread类的构造参数传递。创建Thread类的对象,并执行。
// Thread(Runnable target)
// Thread t1 = new Thread(im);
// Thread t2 = new Thread(im);
// t1.setName("康娜");
// t2.setName("jyh");
// Thread(Runnable target,String name)
Thread t1 =new Thread(im,"康娜");
Thread t2 =new Thread(im,"jyh");
t1.start();
t2.start();
}
}
线程优先级
- 线程优先级级别
线程默认优先级是5。范围是1-10
Thread.MAX_PRIORITY //10
Thread.MIN_PRIORITY //1
Thread.NORM_PRIORITY //5
- 方法
int getPriority()
-- 获取线程优先级。
void setPriority(int pri)
-- 设置线程的优先级。 - 注意
较高的优先级会给线程更多的执行机会,但不保证优先级高的一定先执行。
优先级可以在一定的程度上(即更大的概率)让线程获较多的执行机会
线程调度(了解)
- 分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程的时间
- 抢占式调度模型:随机分配使用权,优先级高的使用几率大一点。
线程控制
线程休眠
package com.lianwei.Thread.ControlThread;
import java.util.Date;
public class ThreadSleep extends Thread{
public void run(){
for (int x=1;x<=10;x++){
System.out.println(getName()+":"+x+"日期:"+new Date());
//睡眠1秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package com.lianwei.Thread.ControlThread;
import com.lianwei.Thread.ExtendsThread.MyThread;
public class ThreadSleepDemo {
public static void main(String[] args) {
//创建线程对象
ThreadSleep mt1 = new ThreadSleep();
ThreadSleep mt2 = new ThreadSleep();
//设置线程名称
mt1.setName("康娜");
mt2.setName("jyh");
//启动线程
mt1.start();
mt2.start();
}
}
线程加入
package com.lianwei.Thread.ControlThread;
import java.util.Date;
public class ThreadJoin extends Thread{
public void run(){
for (int x=1;x<=10;x++){
System.out.println(getName()+":"+x+"日期:"+new Date());
}
}
}
package com.lianwei.Thread.ControlThread;
public class ThreadJoinDemo {
public static void main(String[] args) {
//创建线程对象
ThreadJoin tj1 = new ThreadJoin();
ThreadJoin tj2 = new ThreadJoin();
ThreadJoin tj3 = new ThreadJoin();
tj1.setName("动物");
tj2.setName("猫");
tj3.setName("狗");
tj1.start();
try {
tj1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
tj2.start();
tj3.start();
}
}
线程礼让
package com.lianwei.Thread.ControlThread;
import java.util.Date;
public class ThreadYield extends ThreadJoin{
public void run(){
for (int x=1;x<=10;x++){
System.out.println(getName()+":"+x+"日期:"+new Date());
Thread.yield();
}
}
}
package com.lianwei.Thread.ControlThread;
//public static void yield():暂停当前的线程,并执行其他的线程
//它可以让线程跟和谐,但还是不能保证一人一次
public class ThreadYieldDemo {
public static void main(String[] args){
ThreadYield ty1 = new ThreadYield();
ThreadYield ty2 = new ThreadYield();
ty1.setName("康娜");
ty2.setName("jyh");
}
}
后台线程
package com.lianwei.Thread.ControlThread;
import java.util.Date;
public class ThreadDaemon extends ThreadJoin{
public void run(){
for (int x=1;x<=100;x++){
System.out.println(getName()+":"+x);
}
}
}
package com.lianwei.Thread.ControlThread;
//public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程
//当正在运行的线程都是守护线程时,java虚拟机退出,该方法必须在启动前调用
public class ThreadDaemonDemo {
public static void main(String[] args){
ThreadDaemon td1 = new ThreadDaemon();
ThreadDaemon td2 = new ThreadDaemon();
td1.setName("康娜");
td2.setName("雏鹤爱");
td1.setDaemon(true);
td2.setDaemon(true);
td1.start();
td2.start();
Thread.currentThread().setName("jyh");
for(int x =0;x<=5;x++){
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
中断线程
package com.lianwei.Thread.ControlThread;
import java.util.Date;
public class ThreadStop extends Thread{
public void run(){
System.out.println("开始执行:"+new Date());
//我要休息10秒
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// e.printStackTrace();
System.out.println("线程被终止了");
}
System.out.println("结束执行:"+ new Date());
}
}
package com.lianwei.Thread.ControlThread;
//public final void stop():让线程停止,过时了,但还可以用
//public void interrupt():中断线程。把线程的状态终止,并抛出InterruptedException异常
public class ThreadStopDemo {
public static void main(String[] args){
ThreadStop ts = new ThreadStop();
ts.start();
//超过3秒不好,你就完了
try {
Thread.sleep(3000);
//ts.stop();
ts.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
锁,同步代码块,同步方法
同步机制(锁机制)
同步代码块
格式:
synchronized(锁对象){
需要同步的代码
}
- 作用:
保证在某一时刻只会有一个线程在执行同步代码块中的代码 - 锁对象:
可以是任意对象,但是操作相同资源的锁对象必须是同一个。 - 解释:
线程只有拿到了锁对象,才能执行同步代码块中的代码。换言之,这里的代码如果执行了,说明该线程拿到了锁对象,其他线程就不能拿到该锁对象。
同步方法
- 同步方法:
加了synchronized关键字修饰的方法。 - 锁对象:
非静态方法的锁对象是:this对象。
静态方法的锁对象是:当前类的字节码文件对象。(类名.class)
public synchronized void show(){}
public static synchronized void show(){} - 什么时候用
什么时候用同步代码块,什么时候用同步方法?
尽可能用同步代码块(被同步的内容越少越好)
如果一个方法内的所有代码都被同步代码块包住了,那就用同步方法就可以了
同步的好处及弊端
- 好处:解决了多线程的安全问题
- 弊端:因为每个线程去判断同步上的锁,这降低程序的运行效率会
死锁
死锁原因总结
线程1自身拿着一个锁:A锁,线程2自身拿着一个锁:B锁
当线程1要用B锁,线程B要用A锁的时候就会发生死锁