文章目录
创建线程方法① 继承Thread
继承Thread 重写run()方法
调用启动: 创建对象.start:();
方法 | 返回值 | 说明 |
---|---|---|
(static)sleep(int i) | void | 睡眠 i 毫秒 |
(static)currentThread() | void | 返回当前正在执行的线程,可直接 getName |
getPriority() | int | 获取优先值 返回int |
getName() | String | 获取对象名 |
setName(String str) | void | 给当前线程对象起名 |
setPriority(int i) | void | i:1~10 优先级值 越高表示CPU优先执行概率越高(概率!) |
setDeomon() | void | 将对象线程标记为附属线程,当没有非附属线程运行时,附属线程将自动结束 |
public class Demo {
public static void main(String[] args) throws Exception {
Demo1 d = new Demo1();
d.start(); //开启新线程
for (int i = 0; i < 3; i++) {
System.out.println("主线程执行了");
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
class Demo1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("新线程执行了");
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
运行结果:
主线程执行了
新线程执行了
新线程执行了
主线程执行了
新线程执行了
新线程执行了
主线程执行了
新线程执行了
创建线程方法② 实现Runnable接口
实现Runnable接口 重写run()方法
调用启动: Thread对象有参构造 创建对象.start:();
public class Demo {
public static void main(String[] args) throws Exception {
Demo1 d = new Demo1();
Thread t = new Thread(d);
t.start(); //开启新线程
for (int i = 0; i < 3; i++) {
System.out.println("主线程执行了");
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
class Demo1 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("新线程执行了");
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Lambda表达式 与上方代码完全相同
public class Demo {
public static void main(String[] args) throws Exception {
Thread t = new Thread(() -> { //使用Lambda表达式匿名内部类 实现Runnable接口 重写run();
for (int i = 0; i < 5; i++) {
System.out.println("新线程执行了");
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
});
t.start(); //开启新线程
for (int i = 0; i < 3; i++) {
System.out.println("主线程执行了");
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
运行结果:
主线程执行了
新线程执行了
新线程执行了
主线程执行了
新线程执行了
新线程执行了
主线程执行了
新线程执行了
创建线程方法③ 实现Callable接口
可以获取线程返回值
实现Callable<返回值类型>接口 重写call()方法
调用启动: FutureTask<返回值类型>有参构造创建对象 Thread对象有参构造创建对象.start:();
FutureTask<返回值类型>对象.get(): 等待线程运行结束,返回运行结果
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class Demo {
public static void main(String[] args) throws Exception {
Demo1 d = new Demo1();
FutureTask<String> f = new FutureTask<>(d); //泛型与参数的返回值类型一致
Thread t = new Thread(f);
t.start(); //开启新线程
for (int i = 0; i < 3; i++) {
System.out.println("主线程执行了");
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
String s = f.get();
System.out.println("主线程: 新线程的返回值为" + s);
}
}
class Demo1 implements Callable<String> {
@Override
public String call() throws Exception { //此处返回值为 Callable 的泛型
for (int i = 0; i < 5; i++) {
System.out.println("新线程执行了");
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
return "Demo1";
}
}
运行结果:
主线程执行了
新线程执行了
新线程执行了
主线程执行了
新线程执行了
新线程执行了
主线程执行了
新线程执行了
主线程: 新线程的返回值为Demo1
线程池
Executors
尽量避免使用此方法创建线程池
方法 | 返回值 | 说明 |
---|---|---|
(static)newCachedThreadPool() | 构造方法 | 创建一个默认线程池 |
(static)newFixedThreadPool(int i) | 构造方法 | 创建一个有上限的线程池 |
submit(Runnable r) | void | 开启线程,当线程池中有空线程将不再新建,使用空线程 |
shutdown() | void | 关闭线程池 |
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo {
public static void main(String[] args) throws Exception {
ExecutorService es = Executors.newCachedThreadPool();
System.out.println("主线程执行了");
es.submit(() -> {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
es.submit(() -> System.out.println(Thread.currentThread().getName()));
Thread.sleep(1000);
es.submit(() -> System.out.println(Thread.currentThread().getName()));
es.shutdown();
System.out.println("主线程结束了");
}
}
构造方法:
主线程执行了
pool-1-thread-1
pool-1-thread-2
pool-1-thread-2
主线程结束了
ThreadPoolExecutor
可以指定规则的线程池
用法同Executors
构造方法: ThreadPoolExecutor(核心线程数, 最大线程数, 空闲线程存活时间, 时间单位, 阻塞队列, 线程工厂, 拒绝策略)
参数 | 类型 | 说明 |
---|---|---|
核心线程数 | int | 大于0 |
最大线程数 | int | 大于核心线程数 |
空闲线程存活时间 | int | 大于0 |
时间单位 | TimeUnit | TimeUnit.SECONDS:秒 / DAYS天 / HOURS小时 / MINUTES分钟 / MILLISECONDS毫秒 |
阻塞队列 | ArrayBlockingQueue | new ArrayBlockingQueue<>(int i): 当核心线程满了将存至队列,当队列满了将创建新线程,当线程达到最大值时,将启动拒绝策略 |
线程工厂 | Executors | Executors.defaultThreadFactory(): 创建默认线程 |
拒绝策略 | ThreadPoolExecutor | new ThreadPoolExecutor.AbortPolicy(): 丢弃任务并抛出异常 / DiscardPolicy(): 丢弃任务(不推荐) / DiscardOldestPolicy(): 丢弃队列中等待最久的任务 / CallerRunsPolicy(): 绕过线程直接执行 |
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Demo {
public static void main(String[] args) throws Exception {
ThreadPoolExecutor es = new ThreadPoolExecutor(2, 3, 1, TimeUnit.SECONDS, new ArrayBlockingQueue<>(2), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
System.out.println("主线程执行了");
es.submit(() -> {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
es.submit(() -> System.out.println(Thread.currentThread().getName()));
Thread.sleep(1000);
es.submit(() -> System.out.println(Thread.currentThread().getName()));
es.shutdown();
System.out.println("主线程结束了");
}
}
运行结果:
主线程执行了
pool-1-thread-1
pool-1-thread-2
pool-1-thread-2
主线程结束了
线程操作
CountDownLatch
控制一个线程等待其他线程各自执行完毕后再执行
方法 | 返回值 | 说明 |
---|---|---|
CountDownLatch(int i) | 构造方法 | 原理为计数器,初始为 i ,每当一个线程执行完毕计数器 -1 ,当计数器为 0 时,唤醒所有等待中的线程 |
await() | void | 使当前线程等待,当计数器为0时唤醒 |
countDown() | void | 表示线程执行完毕 |
import java.util.concurrent.CountDownLatch;
public class Demo {
private static CountDownLatch c = new CountDownLatch(2);
public static void main(String[] args) throws Exception {
Thread t1 = new Thread(() -> { //新线程 1
try {
Thread.sleep(2000); //等待2秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("新线程 1: 执行了");
c.countDown();
});
Thread t2 = new Thread(() -> { //新线程 2
System.out.println("新线程 2: 执行了");
try {
Thread.sleep(2000); //等待2秒
} catch (InterruptedException e) {
e.printStackTrace();
}
c.countDown();
});
t1.start(); //开启新线程 1
t2.start(); //开启新线程 2
System.out.println("主线程: 执行了");
c.await();
System.out.println("主线程: 两条新线程执行完毕");
}
}
运行结果:
主线程: 执行了
新线程 2: 执行了
新线程 1: 执行了
主线程: 两条新线程执行完毕
Semaphore
控制同时执行的线程数量
Semaphore(int i) | | 原理为计数器,初始为 i ,允许同时执行的线程数量, i 为 0 则不允许获取执行权限
acquire() | | 获取执行权限 i-1
release() | | 归还执行权限 i+1
import java.util.concurrent.Semaphore;
public class Demo {
private static Semaphore s = new Semaphore(3);
public static void main(String[] args) throws Exception {
Thread t1 = new Thread(() -> { //新线程 1
try {
s.acquire();
System.out.println("新线程 1: 执行了");
Thread.sleep(2000); //等待2秒
System.out.println("新线程 1: 执行完毕");
s.release();
} catch (Exception e) {
e.printStackTrace();
}
});
Thread t2 = new Thread(() -> { //新线程 2
try {
s.acquire();
System.out.println("新线程 2: 执行了");
Thread.sleep(2000); //等待2秒
System.out.println("新线程 2: 执行完毕");
s.release();
} catch (Exception e) {
e.printStackTrace();
}
});
Thread t3 = new Thread(() -> { //新线程 3
try {
s.acquire();
System.out.println("新线程 3: 执行了");
Thread.sleep(2000); //等待2秒
System.out.println("新线程 3: 执行完毕");
s.release();
} catch (Exception e) {
e.printStackTrace();
}
});
Thread t4 = new Thread(() -> { //新线程 4
try {
s.acquire();
System.out.println("新线程 4: 执行了");
Thread.sleep(2000); //等待2秒
System.out.println("新线程 4: 执行完毕");
s.release();
} catch (Exception e) {
e.printStackTrace();
}
});
Thread t5 = new Thread(() -> { //新线程 5
try {
s.acquire();
System.out.println("新线程 5: 执行了");
Thread.sleep(2000); //等待2秒
System.out.println("新线程 5: 执行完毕");
s.release();
} catch (Exception e) {
e.printStackTrace();
}
});
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
System.out.println("主线程: 执行了");
}
}
运行结果:
主线程: 执行了
新线程 3: 执行了
新线程 1: 执行了
新线程 2: 执行了
新线程 3: 执行完毕
新线程 2: 执行完毕
新线程 1: 执行完毕
新线程 4: 执行了
新线程 5: 执行了
新线程 4: 执行完毕
新线程 5: 执行完毕
可以看到最多同时执行 3 条线程,即使中间等待了 2秒 ,其他线程也无法执行
安全问题
在不同线程同时操作一个数据,可能会出现线程安全问题
抢票案例:
public class Demo {
public static int tickets = 10; //一共10张票
public static void main(String[] args) throws Exception {
new Demo1().start();
new Demo1().start();
new Demo1().start();
new Demo1().start();
new Demo1().start(); //开启5个抢票线程
Thread.sleep(5000);
}
}
class Demo1 extends Thread {
@Override
public void run() {
while (true) {
if (Demo.tickets > 0) {
System.out.println("线程" + Thread.currentThread().getName() + ": 抢票");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
Demo.tickets = Demo.tickets - 1;
System.out.println("线程" + Thread.currentThread().getName() + ": 抢到了,还有" + Demo.tickets + "张");
} else {
System.out.println("线程" + Thread.currentThread().getName() + ": 没票了");
break;
}
}
}
}
运行结果:
线程Thread-4: 抢票
线程Thread-3: 抢票
线程Thread-2: 抢票
线程Thread-1: 抢票
线程Thread-0: 抢票
线程Thread-2: 抢到了,还有6张
线程Thread-1: 抢到了,还有6张
线程Thread-3: 抢到了,还有6张
线程Thread-0: 抢到了,还有6张
线程Thread-0: 抢票
线程Thread-4: 抢到了,还有6张
线程Thread-4: 抢票
线程Thread-3: 抢票
线程Thread-1: 抢票
线程Thread-2: 抢票
线程Thread-1: 抢到了,还有3张
线程Thread-1: 抢票
线程Thread-0: 抢到了,还有1张
线程Thread-0: 抢票
线程Thread-3: 抢到了,还有2张
线程Thread-3: 抢票
线程Thread-2: 抢到了,还有3张
线程Thread-2: 抢票
线程Thread-4: 抢到了,还有2张
线程Thread-4: 抢票
线程Thread-4: 抢到了,还有-1张
线程Thread-4: 没票了
线程Thread-2: 抢到了,还有-2张
线程Thread-2: 没票了
线程Thread-0: 抢到了,还有-2张
线程Thread-0: 没票了
线程Thread-1: 抢到了,还有-3张
线程Thread-1: 没票了
线程Thread-3: 抢到了,还有-2张
线程Thread-3: 没票了
很明显不可能还剩-2张票
线程锁
synchronized(任意对象){ 共享数据操作代码块 }
将代码块锁上,参数对象相当于钥匙,只有一把钥匙,即被相同锁锁上的代码同一时间只能有一份在执行
public class Demo {
public static int tickets = 10; //一共10张票
public static String s = "一个字符串";
public static void main(String[] args) throws Exception {
new Demo1().start();
new Demo1().start();
new Demo1().start();
new Demo1().start();
new Demo1().start(); //开启5个抢票线程
Thread.sleep(5000);
}
}
class Demo1 extends Thread {
@Override
public void run() {
while (true) {
synchronized (Demo.s) { //使用一个字符串对象当锁
if (Demo.tickets > 0) {
System.out.println("线程" + Thread.currentThread().getName() + ": 抢票");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
Demo.tickets = Demo.tickets - 1;
System.out.println("线程" + Thread.currentThread().getName() + ": 抢到了,还有" + Demo.tickets + "张");
} else {
System.out.println("线程" + Thread.currentThread().getName() + ": 没票了");
break;
}
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果:
线程Thread-0: 抢票
线程Thread-0: 抢到了,还有9张
线程Thread-4: 抢票
线程Thread-4: 抢到了,还有8张
线程Thread-3: 抢票
线程Thread-3: 抢到了,还有7张
线程Thread-2: 抢票
线程Thread-2: 抢到了,还有6张
线程Thread-1: 抢票
线程Thread-1: 抢到了,还有5张
线程Thread-2: 抢票
线程Thread-2: 抢到了,还有4张
线程Thread-3: 抢票
线程Thread-3: 抢到了,还有3张
线程Thread-4: 抢票
线程Thread-4: 抢到了,还有2张
线程Thread-0: 抢票
线程Thread-0: 抢到了,还有1张
线程Thread-4: 抢票
线程Thread-4: 抢到了,还有0张
线程Thread-3: 没票了
线程Thread-2: 没票了
线程Thread-1: 没票了
线程Thread-0: 没票了
线程Thread-4: 没票了
ReentrantLock
方法 | 返回值 | 说明 |
---|---|---|
Lock() | void | 获得锁 |
tryLock() | void | 尝试获得锁 |
unLock() | void | 释放锁 |
虚拟机中线程的六种状态:
NEW ( 新建 ) -> 创建线程对象
RUNNABLE ( 就绪 ) -> start();
BLOCKED ( 阻塞 ) -> 无法进入被锁对象
WAITING ( 等待 ) -> wait();
TIME_WAITING ( 计时 ) -> sleep();
TERMINATED ( 结束 ) -> 运行完毕
原子性
原子性操作有: ①除long和double之外的原始类型的赋值 ②java.concurrent.Atomic.*类的操作
AtomicInteger
以原子方式操作int
方法 | 返回值 | 说明 |
---|---|---|
AtomicInteger() | 构造方法 | 初始化默认值为0 |
AtomicInteger(int i) | 构造方法 | 初始化为 i |
get() | int | 获取值 |
getAndIncrement() | int | 以原子方式自增1 返回自增前 |
incrementAndGet() | int | 以原子方式自增1 返回自增后 |
addAndGet(int i) | int | 以原子方式增加i 返回结果 |
getAndSet(int i) | int | 以原子方式改为i 返回旧 |
Hashtable: 用法同HashMap 底层synchronized加HashMap(效率较低)
ConcurrentHashMap: 用法同HashMap(效率较高)
BlockingQueue
阻塞队列
方法 | 返回值 | 说明 |
---|---|---|
ArrayBlockingQueue | 构造方法 | 底层数组,有界 |
LinkedBlockingQueue | 构造方法 | 底层链表.无界 |
put(T t) | void | 存入元素 /类似集合,但若存入时队列满了则等待队列有空余再存入 |
take() | T | 取出元素 /类似集合,但若取出时队列为空则等待队列有内容时再取出 |