1.并发
同一时刻只能有一个线程执行,但是多个线程被快速的轮换执行,使得在宏观上具有多个线程同时执行的效果,但在微观上并不是同时执行的,只是把CPU运行时间划分为若干个时间段,再将时间段分配给线程执行。
2.进程
作为资源分配的单位。再操作系统中能同时运行多个任务(程序),系统在运行的时候会为每个进程分配不同的内存区域。没有线程的进程是可以看作单线程的。如果一个进程内拥有多个线程,则执行过程不是一条线,而是多条线(线程)共同完成。
3.线程
调度和执行的单位。线程可以看成轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。除了CPU 外,不会为线程分配内存(线程所使用的资源是它所属的进程的资源),线程内只能共享资源。线程是进程的一部分。
*总结
-
一个应用程序至少对应一个进程。
-
一个进程可以包含多个线程。
-
一个程序关闭了,在系统中的进程就没了,程序中的线程也结束了。
5.创建线程的三种方法
1.第一种
-
package com.yang.thread;
/*
[1] 继承Thread类,重写run方法
[2] 使用start() 开启子线程
[3] 我们调用start(),底层调用的是start0(),底层不是java书写的--->run().
*/
//线程实现的第一种方式
public class Thread01 {
public static void main(String[] args) {
ThreadTest threadTest = new ThreadTest();
threadTest.setName("兔子");//给线程设置名字
threadTest.start();//开启线程
ThreadTest threadTest1 = new ThreadTest();
threadTest1.setName("乌龟");
threadTest1.start();
}
}
class ThreadTest extends Thread{//继承线程
package com.yang.thread;
public class Thread02 {
public static void main(String[] args) {
//匿名内部类
Thread thread = new Thread(){
2.第二种
package com.yang.thread;
//创建线程的第二种方法
/*
实现Runnable接口,重写run方法。
直接调用start()不可以开启线程。需要使用Thread有参构造进行开启线程。
*/
public class Thread03 {
public static void main(String[] args) {
//创建两个对象,和创建一个对象的区别在于资源是否共享。
MyThread my = new MyThread();
MyThread my1 = new MyThread();
Thread t = new Thread(my,"兔子");
Thread t1 = new Thread(my1,"乌龟");
t.start();
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
/*
有三个线程,一个主线程main,还有两个子线程,,他们都是同时运行的。只是有主线程运行的快,所以先输出完。
*/
System.out.println("我在输出语句了");
}
}
class MyThread implements Runnable{
package com.yang.thread;
public class Thread04 {
public static void main(String[] args) {
//使用匿名内部类
new Thread(new Runnable() {
3.第三种
package com.yang.thread;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//实现线程的第三种方式
//实现Callable
/*
可以有返回值
*/
//底层原理
//start() --> start0()-->run()->FutureTask 中的run()方法中调的是-->result = c.call()-->set(result)
// -->outcome = v返回值-->get()得到返回值。
public class Demo05 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable my = new MyCallable();
MyCallable my1 = new MyCallable();//这个一般写两个;主要看资源是不是共享
// FutureTask(Callable<V> callable)--->FutureTask<V> implements RunnableFuture<V>--->RunnableFuture<V> extends Runnable
//相等于一个中间转换FutureTask
//这是开启了两个线程
FutureTask<Integer> task = new FutureTask<>(my);
FutureTask<Integer> task1 = new FutureTask<>(my1);
Thread thread = new Thread(task,"兔子");
Thread thread1 = new Thread(task,"兔子");
thread.start();
thread1.start();
//获得当前线程结束后的返回值。thread.start();不能写在这之后,理解一下。
Integer integer = task.get();
Integer integer1 = task1.get();
System.out.println(integer);
System.out.println(integer1);
}
}
class MyCallable implements Callable<Integer> {
package com.yang.thread;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Demo06 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> futureTask = new FutureTask<>(new Callable<Integer>() {
*总结
-
后两种方式可以避免Java中的单继承的局限性
-
需要返回值只能使用想实现Callable接口
-
不需要返回值推荐使用Runnable接口
-
线程池只能使用Runnable或者Callable类型参数,不能使用Thread类。
6. LOCK
6.1 验证lock锁是否时可重入锁
package com.yang.lock;
import java.util.concurrent.locks.ReentrantLock;
//验证Lock可重入锁
public class Demo06 {
public static void main(String[] args) {
ReentrantLock y = new ReentrantLock();//遥控器锁
ReentrantLock d = new ReentrantLock();//电池锁
Thread th = new Thread(new XiaoMing1(y,d));
Thread th1 = new Thread(new XiaoHong2(y,d));
th.start();
th1.start();
}
}
class XiaoMing1 implements Runnable{
private ReentrantLock ykq;
private ReentrantLock dc;
public XiaoMing1(ReentrantLock y, ReentrantLock d) {
this.ykq = y;
this.dc = d;
}
6.2 Lock锁
package com.yang.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
Lock锁:
1] 多个线程使用时一定要保证同一个锁对象
2]lock.lock()加锁。lock.unlock()解锁。
3]如果代码出现了异常,程序不会自动解锁,所以使用Lock的时候 最好写到try catch finally代码中
*/
public class Demo05 {
public static void main(String[] args) {
//两个对象
Thread th = new Thread(new TicketTwo(),"窗口A");
Thread th1 = new Thread(new TicketTwo(),"窗口B");
th.start();
th1.start();
}
}
class TicketTwo implements Runnable {
static int num = 1;
static Lock lock = new ReentrantLock();//创建Lock锁 两个对象加一个static
7.Lock和synchronized的选择
-
类型不同
-
synchronized 是关键字,修饰方法,修饰代码块
-
Lock是接口
-
-
加锁和解锁机制同步
-
synchronized 是自动枷锁和解锁,程序员不需要控制
-
Lock 是需要程序员手动加锁和解锁,要注意的是:出现异常不会自动解锁的情况。
-
-
异常机制
-
synchronized 遇到异常会自动解锁,不会出现死锁的情况
-
Lock 不会自动解锁,遇到异常可能会出现死锁的情况,所以要放到finally()中。
-
-
Lock功能更强大
-
Lock中可以有tryLock()/ isLock()方法判断是否上锁。synchronized 不可以。
-
-
Lock锁性能更优
-
如果多线程竞争锁,Lock更好,如果线程不多差距不大
-
-
锁的内容不同
-
synchronized 可以锁方法,锁代码块
-
lock只可以锁代码块
-
-
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· 单线程的Redis速度为什么快?
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码