多线程
实现多线程方式一: 继承Thread类
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println(i);
}
}
}
//=============================================
public class Test1 {
public static void main(String[] args) {
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
mt1.start();
mt2.start();
}
}
注意
1、为什么要重写run()方法
因为run()是用来封装杯线程执行的代码
2、run方法和start()方法的区别
run():封装线程执行的代码,直接调用,相当于普通方法调用
start():启动线程;由JVM调用此线程的run()方法
实现多线程方式二: 实现Runnable接口
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}
//=================================
public class Test1 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable);
Thread t2 = new Thread(myRunnable);
t1.start();
t2.start();
}
}
实现多线程方式二: 实现Callable接口
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 0; i <= 10; i++) {
System.out.println(i);
}
return "好的";
}
}
public class Test1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable();
FutureTask<String> futureTask = new FutureTask<String>(myCallable);
Thread t1 = new Thread(futureTask);
t1.start();
String s = futureTask.get();
System.out.println(s);
}
}
Thread构造方法:
| 方法名 | 说明 |
|---|---|
| Thread(Runnable target) | 分配一个新的Thread对象 |
| Thread(Runnable target, String name) | 分配一个新的Thread对象 |
Thread常用方法:
| 方法名 | 说明 |
|---|---|
| void setName(String name) | 将此线程的名称更改为等于参数name |
| String getName() | 返回此线程的名称 |
| Thread currentThread() | 返回对当前正在执行的线程对象的引用 |
public class MyThread extends Thread {
public MyThread() {}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName()+i);
}
}
}
public class Test1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread("小红");
myThread1.setName("小明");
myThread1.start();
myThread2.start();
System.out.println(Thread.currentThread().getName());
}
}
线程休眠
| 方法名 | 说明 |
|---|---|
| static void sleep(long millis) | 使当前正在执行的线程停留(暂停执行)指定的毫秒数 |
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}
public class Test1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable);
Thread t2 = new Thread(myRunnable);
t1.start();
t2.start();
}
}
java优先级
线程调度:
1、分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
2、抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些
注意:
Java使用的是抢占式调度模型
相关方法
| 方法名 | 说明 |
|---|---|
| final int getPriority() | 返回此线程的优先级 |
| final void setPriority(int newPriority) | 更改此线程的优先级线程默认优先级是5;线程优先级的范围是:1-10 |
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}
public class Test1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable);
Thread t2 = new Thread(myRunnable);
System.out.println(t1.getPriority());
t2.setPriority(10);
t1.start();
t2.start();
}
}
守护线程
守护线程是一种特殊的线程。当进程中不存在非守护线程时,则守护线程自动销毁
| 方法名 | 说明 |
|---|---|
| void setDaemon(boolean on) | 将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出 |
线程同步
同步代码块格式:
synchronized(任意对象) {
多条语句操作共享数据的代码
}
synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁
同步方法的格式:
修饰符 synchronized 返回值类型 方法名(方法参数) {
方法体;
}
同步方法的锁对象一般是: this
静态同步方法:
修饰符 static synchronized 返回值类型 方法名(方法参数) {
方法体;
}
同步静态方法锁对象一般是 类名.class
Lock锁
Lock是接口不能直接实例化,可以用它的实现类ReentrantLock来实例化
构造方法:
| 方法名 | 说明 |
|---|---|
| ReentrantLock() | 创建一个ReentrantLock的实例 |
加锁解锁方法
| 方法名 | 说明 |
|---|---|
| void lock() | 获得锁 |
| void unlock() | 释放锁 |
生产者和消费者模式
生产者消费者模式是一个十分经典的多线程协作的模式,
一类是生产者线程用于生产数据
一类是消费者线程用于消费数据
为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库
生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为
消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为
Object类的等待和唤醒方法
| 方法名 | 说明 |
|---|---|
| void wait() | 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法 |
| void notify() | 唤醒正在等待对象监视器的单个线程 |
| void notifyAll() | 唤醒正在等待对象监视器的所有线程 |
阻塞队列

常见BlockingQueue:
ArrayBlockingQueue: 底层是数组,有界
LinkedBlockingQueue: 底层是链表,无界(最大为int的最大值)
BlockingQueue的核心方法:
put(anObject): 将参数放入队列,如果放不进去会阻塞
take(): 取出第一个数据,取不到会阻塞
线程状态
| 线程状态 | 具体含义 |
|---|---|
| NEW | 一个尚未启动的线程的状态。也称之为初始状态、开始状态。线程刚被创建,但是并未启动。还没调用start方法。MyThread t = new MyThread()只有线程象,没有线程特征。 |
| RUNNABLE | 当我们调用线程对象的start方法,那么此时线程对象进入了RUNNABLE状态。那么此时才是真正的在JVM进程中创建了一个线程,线程一经启动并不是立即得到执行,线程的运行与否要听令与CPU的调度,那么我们把这个中间状态称之为可执行状态(RUNNABLE)也就是说它具备执行的资格,但是并没有真正的执行起来而是在等待CPU的度。 |
| BLOCKED | 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。 |
| WAITING | 一个正在等待的线程的状态。也称之为等待状态。造成线程等待的原因有两种,分别是调用Object.wait()、join()方法。处于等待状态的线程,正在等待其他线程去执行一个特定的操作。例如:因为wait()而等待的线程正在等待另一个线程去调用notify()或notifyAll();一个因为join()而等待的线程正在等待另一个线程结束。 |
| TIMED_WAITING | 一个在限定时间内等待的线程的状态。也称之为限时等待状态。造成线程限时等待状态的原因有三种,分别是:Thread.sleep(long),Object.wait(long)、join(long)。 |
| TERMINATED | 一个完全运行完成的线程的状态。也称之为终止状态、结束状态 |
线程池
线程池在启动的时,会创建大量空闲线程,当我们向线程池提交任务的时,线程池就会启动一个线程来执行该任务。等待任务执行完毕以后,线程并不会死亡,而是再次返回到线程池中称为空闲状态。等待下一次任务的执行。
Executors默认线程池
// 创建一个默认的线程池
static ExecutorService newCachedThreadPool()
//创建一个指定最多线程数量的线程池
static newFixedThreadPool(int nThreads)
public class Test2 {
public static void main(String[] args) throws InterruptedException {
//使用Executors的静态方法创建线程池
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(()-> System.out.println(Thread.currentThread().getName()));
executorService.submit(()-> System.out.println(Thread.currentThread().getName()));
//关闭线程池
executorService.shutdown();
Thread.sleep(300);
System.out.println("===============");
//使用Executors的静态方法创建线程池(指定最多线程数量的线程池)
//最大值是10,但不是初始值
ExecutorService executorService1 = Executors.newFixedThreadPool(10);
ThreadPoolExecutor pool = (ThreadPoolExecutor) executorService1;
//看看线程池中 线程数 0
System.out.println(pool.getPoolSize());
executorService1.submit(()-> System.out.println(Thread.currentThread().getName()));
executorService1.submit(()-> System.out.println(Thread.currentThread().getName()));
//此时数量为2
System.out.println(pool.getPoolSize());
//关闭线程池
executorService1.shutdown();
}
}
Volatile关键字
强制线程每次在使用的时候,都会看一下共享区域最新的值
原子性Atomic包
java从JDK1.5开始提供了java.util.concurrent.atomic包(简称Atomic包),这个包中的原子操作类提供了一种用法简单,性能高效,线程安全地更新一个变量的方式。在Atomic包里一共提供了13个类,属于4种类型的原子更新方式,分别是原子更新基本类型、原子更新数组、原子更新引用和原子更新属性(字段)
例:AtomicInteger: 原子更新整型
public AtomicInteger(): 初始化一个默认值为0的原子型Integer
public AtomicInteger(int initialValue): 初始化一个指定值的原子型Integer
int get(): 获取值
int getAndIncrement(): 以原子方式将当前值加1,注意,这里返回的是自增前的值。
int incrementAndGet(): 以原子方式将当前值加1,注意,这里返回的是自增后的值。
int addAndGet(int data): 以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果。
int getAndSet(int value): 以原子方式设置为newValue的值,并返回旧值。
AtomicInteger原理 : 自旋锁 + CAS 算法
有3个操作数(内存值V, 旧的预期值A,要修改的值B)
当旧的预期值A == 内存值 此时修改成功,将V改为B
当旧的预期值A!=内存值 此时修改失败,不做任何操作
并重新获取现在的最新值(这个重新获取的动作就是自旋)
悲观锁和乐观锁
synchronized和CAS的区别 :
相同点:在多线程情况下,都可以保证共享数据的安全性。
不同点:synchronized总是从最坏的角度出发,认为每次获取数据的时候,别人都有可能修改。所以在每次操作共享数据之前,都会上锁。(悲观锁)
cas是从乐观的角度出发,假设每次获取数据别人都不会修改,所以不会上锁。只不过在修改共享数据的时候,会检查一下,别人有没有修改过这个数据。
如果别人修改过,那么我再次获取现在最新的值。
如果别人没有修改过,那么我现在直接修改共享数据的值.(乐观锁)
并发工具类
HashMap是线程不安全的。多线程环境下会有数据安全问题
Hashtable是线程安全的,但是会将整张表锁起来,效率低下
ConcurrentHashMap也是线程安全的,效率较高。
在JDK7和JDK8中,底层原理不一样。
public class MyConcurrentHashMapDemo {
public static void main(String[] args) throws InterruptedException {
ConcurrentHashMap<String, String> hm = new ConcurrentHashMap<>(100);
Thread t1 = new Thread(() -> {
for (int i = 0; i < 25; i++) {
hm.put(i + "", i + "");
}
});
Thread t2 = new Thread(() -> {
for (int i = 25; i < 51; i++) {
hm.put(i + "", i + "");
}
});
t1.start();
t2.start();
System.out.println("----------------------------");
//为了t1和t2能把数据全部添加完毕
Thread.sleep(1000);
//0-0 1-1 ..... 50- 50
for (int i = 0; i < 51; i++) {
System.out.println(hm.get(i + ""));
}//0 1 2 3 .... 50
}
}
CountDownLatch类
| 方法 | 解释 |
|---|---|
| public CountDownLatch(int count) | 参数传递线程数,表示等待线程数量 |
| public void await() | 让线程等待 |
| public void countDown() | 当前线程执行完毕 |
Semaphore类
可以控制访问特定资源的线程数量

本文深入探讨Java多线程,包括线程同步、Lock锁、生产者消费者模式、阻塞队列、线程池、Volatile关键字以及Atomic包等核心概念。详细介绍了各种线程同步机制和并发工具类的使用,帮助理解Java并发编程的关键知识点。

485

被折叠的 条评论
为什么被折叠?



