进程
正在运行的程序,是系统进行资源分配和调用的独立单位
每一个进程都有它自己的内存空间和系统资源
线程
是进程中的单个顺序控制流,或者说就是一个单独执行路径
一个进程如果只有一条执行路径,称之为单线程程序
一个进程如果有多条执行路径,称之为多线程程序
线程是包含在进程中的
并行
指的是逻辑上同时发生,指在某一时间段内同时运行多个程序
并发
指的是物理上同时发生,指在某一时间点上同时运行多个程序
Java程序的运行原理
java 命令会启动 java 虚拟机,启动 JVM,
等于启动了一个应用程序,也就是启动了一个进程
该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法
JVM启动时是多线程
至少包括主程序和垃圾回收程序
步骤:
1、自定义一个子类MyThread1继承Thread类
2、自定义类重写Thread类中的 run() 方法
3、写入需要被线程执行的代码
4、创建子类对象
5、启动线程 start ()
public class MyThread1 extends Thread{
public MyThread1(){
}
public MyThread1(String name){
super(name);
}
@Override
public void run() {
for(int i = 0; i < 100; i++){
System.out.println("韭菜盒子");
}
}
}
MyThread1 myThread1 = new MyThread1("fgh");
MyThread1 myThread2 = new MyThread1("xl");
// myThread1.run();
// run()的调用仅仅是封装了被线程执行的代码
// 但是直接调用的话是普通的方法调用
// start()方法的调用,首先单独启动了一个线程
// 然后再由JVM去调用该线程的run()方法
// 模拟多线程
myThread1.start();
myThread2.start();
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
for(int i = 1; i <= 100; i++){
System.out.println(getName()+"---"+i);
}
}
}.start();
步骤:
1、创建自定义类实现 Runnable 接口
2、重写 run() 方法
3、创建自定义类的对象
4、创建Thread类的对象,将第三步创建的自定义类对象作为参数传递到构造方法中
public class MyRunnable1 implements Runnable {
@Override
public void run() {
for(int i = 0; i < 100; i++){
// 由于实现的Runnable接口中并没有getName()方法,
// 所以这里无法使用Thread类中的getName()方法
// 间接调用,因为currentThread()是静态的,
// 可以直接通过类名调用获取当前正在
// 执行run方法的线程对象(Thread类型),所以可以调用getName()
System.out.println(Thread.currentThread().getName()+"韭菜盒子");
}
}
}
// 此处只需要创建一个 Runnable 对象即可
MyRunnable1 myRunnable1 = new MyRunnable1();
// 利用该对象创建两个线程
Thread thread1 = new Thread(myRunnable1);
Thread thread2 = new Thread(myRunnable1);
thread1.setName("小六");
thread2.setName("fgh");
thread1.start();
thread2.start();
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 1; i <= 100; i ++){
System.out.println(Thread.currentThread()
.getName()+"---"+i);
}
}
}).start();
}
步骤:
1、创建自定义类实现 Callable 接口
2、重写 call() 方法
3、创建线程池对象
4、创建自定义类对象
5、通过 submit() 方法,创建自定义类对象线程
public class Callable1 implements Callable {
@Override
public Object call() throws Exception {
for(int i = 1; i <= 100; i++){
System.out.println(Thread.currentThread().getName()+"----"+i);
}
return null;
}
}
ExecutorService ex = Executors.newFixedThreadPool(2);
ex.submit(new Callable1());
ex.submit(new Callable1());
ex.shutdown();
1、赋名
可以利用构造方法赋名,一可利用以下方法
public final void setName(String name)
MyThread1 myThread1 = new MyThread1();
myThread1.setName("fgh");
2、获取名字
public final String getName()
String name = myThread1.getName();
3、休眠
冷却
public static void sleep(long millis)
Thread.sleep(500);
myThread1.sleep(1000);
4、优先级
多线程中是按照抢占式执行的,其中一个很重要的参照即优先级。范围 [1,10]
注:优先级高的线程未必绝对优先,参考抽卡的欧洲人
4.1 获取线程优先级
public final int getPriority()
int priority = myThread1.getPriority();
System.out.println(priority);
输出结果:5
默认优先级为 5
4.2 设置线程优先级
public final void setPriority(int newPriority)
myThread1.setPriority(10);
线程范围 [1,10]
5、比翼双飞
让线程运行一定程度均匀
public static void yield()
Thread.yield();
myThread1.yield();
6、中断
6.1 stop
public final void stop()
中断线程,安全问题已弃用
6.2 interrupt
public void interrupt()
设置中断标记,并不真正中断线程
7、等待死亡
public final void join()
其他线程等待这个线程死亡,即优先运行
myThread1.start();
myThread1.join();
myThread2.start();
8、后台线程:(守护线程)
Java中有两类线程:用户线程、守护线程
用户线程:我们在学习多线程之前所有程序代码,运行起来都是一个个的用户线程
守护线程:所谓的守护线程,指的就是程序运行的时候在后台提供了一种通用服务的线程
如垃圾回收线程,它就是一个称职的守护者,并且这种线程并不属于程序不可或缺的部分
非守护线程结束的时候,程序也就终止了,同时会杀死进程种所有的守护线程
当运行的唯一线程都是守护进程线程时,Java虚拟机将退出
设置守护线程
public final void setDaemon(boolean on)
将线程设置为守护线程这一步骤,必须在启动前设置
myThread1.setDaemon(true);
myThread1.start();
myThread2.start();
把多个线程组合到一起
Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,
Java允许程序直接对线程组进行控制的
Runnable1 runnable1 = new Runnable1();
Thread thread1 = new Thread(runnable1,"小六");
Thread thread2 = new Thread(runnable1,"fgh");
// 默认情况下,所有的线程都属于主线程组。
// public final ThreadGroup getThreadGroup()
ThreadGroup threadGroup1 = thread1.getThreadGroup();
ThreadGroup threadGroup2 = thread2.getThreadGroup();
// public final String getName()返回此线程组的名称
System.out.println(threadGroup1.getName());
System.out.println(threadGroup2.getName());
// main方法同样属于主线程组
System.out.println(Thread.currentThread().getThreadGroup().getName());
// Thread(ThreadGroup group, Runnable target, String name)
// 创建新线程加入线程分组
// 先创建一个新的线程组
// ThreadGroup(String name)
ThreadGroup tg = new ThreadGroup("new group");
Thread thread3 = new Thread(tg,runnable1,"小六");
Thread thread4 = new Thread(tg,runnable1,"fgh");
System.out.println(thread3.getThreadGroup().getName());
System.out.println(thread4.getThreadGroup().getName());
// 直接通过组名设置一个组都是守护线程组,组里面的线程都是守护线程
tg.setDaemon(true);
线程池里的每一个线程代码结束后,并不会死亡,
而是再次回到线程池中成为空闲状态,等待下一个对象来使用
创建线程池对象,Executors 工厂类下的静态方法
newFixedThreadPool 是其中一种线程池
public static ExecutorService newFixedThreadPool(int nThreads)
// ExecutorService pool = Executors.newFixedThreadPool(2);
// 创建一个线程池对象
ExecutorService ex = Executors.newFixedThreadPool(2);
// Future> submit(Runnable task)
// 提交一个可运行的任务执行,并返回一个表示该任务的未来
Runnable1 runnable1 = new Runnable1();
// 添加两个线程
ex.submit(runnable1);
ex.submit(runnable1);
// 结束线程池
ex.shutdown();
// 线程池已经被关闭,不能再重复提交运行。
// ex.submit(myRunnable2);
两个或者两个以上的线程在争夺资源的过程中,出现了相互等待的现象,叫死锁现象
public class Dielock1 extends Thread {
private boolean flag;
public Dielock1(boolean flag) {
this.flag = flag;
}
// 创建两个普通类对象
public static final Object lock1 = new Object();
public static final Object lock2 = new Object();
@Override
public void run() {
if (flag) {
synchronized (lock1) {
System.out.println("1-1锁");
synchronized (lock2) {
System.out.println("1-2锁");
}}
} else {
synchronized (lock2) {
System.out.println("2-1锁");
synchronized (lock1) {
System.out.println("2-2锁");
}}
}
}
}
Dielock1 dielock1 = new Dielock1(true);
Dielock1 dielock2 = new Dielock1(false);
dielock1.start();
dielock2.start();
运行结果: 1-1锁
2-1锁
synchronized 就像是劫持了一位对象作为人质
在该同步方法运行完之前,所劫持的人质(对象)不能被释放(其他代码调用)
import java.util.Timer;
import java.util.TimerTask;
/*
定时器是一个应用十分广泛的线程工具,
可用于调度多个定时任务以后台线程的方式执行。
在Java中,可以通过Timer和TimerTask类来实现定义调度的功能
Timer() 创建一个新的计时器。
void schedule(TimerTask task, long delay)
在指定的延迟之后安排指定的任务执行。
void schedule(TimerTask task, long delay, long period)
在指定 的延迟之后开始 ,重新执行 固定延迟执行的指定任务。
void cancel() 终止此计时器,丢弃任何当前计划的任务。
TimerTask :任务,抽象类,需要继承 run() 方法
*/
public class TimerDemo {
public static void main(String[] args) {
//创建定时器对象
Timer timer = new Timer();
//调度任务执行
//void schedule(TimerTask task, long delay)
在指定的延迟之后安排指定的任务执行。
// timer.schedule(new MyTask(),3);
// timer.cancel();
timer.schedule(new MyTask(timer),3000);
}
}
class MyTask extends TimerTask{
private Timer timer;
public MyTask() {
}
public MyTask(Timer timer){
this.timer = timer;
}
@Override
public void run() {
System.out.println("bong!!");
timer.cancel();
}
}
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
/*
void schedule(TimerTask task, long delay, long period)
在指定 的延迟之后开始 ,重新执行 固定延迟执行的指定任务。
*/
public class TimerDemo2 {
public static void main(String[] args) {
//创建定时器对象
Timer timer = new Timer();
//3秒钟执行,每个两秒钟一次
timer.schedule(new MyTask2(),3000,2000);
}
}
class MyTask2 extends TimerTask{
@Override
public void run() {
try {
FileReader fileReader = new FileReader("a3.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
String s = bufferedReader.readLine();
System.out.println(s);
} catch (IOException e) {
e.printStackTrace();
}
}
}
多线程的实现方式:
1、继承Thread类,重写run()方法,调用start()方法启动
2、实现Runnable接口,重写run()方法,创建Thread对象
把Runnable对象当作参数传递,调用start()方法启动
3、实现Callable接口,重写call()方法,提交到线程池中运行,需要结合线程池