1. 进程和线程-开启线程的三种方式
1.1 进程和线程的介绍
1.2 开启线程的三种方式
1.2.1 继承Thread类
package com.itheima.thread;
public class ThreadDemo1 {
/*
开启线程第一种方式: 继承Thread类
1. 编写一个类继承Thread
2. 重写run方法
3. 将线程任务代码写在run方法中
4. 创建线程对象
5. 调用start方法开启线程
细节: 调用start方法开启线程, 会自动调用run方法执行.
注意: 只有调用了start方法, 才是开启了新的线程
*/
public static void main(String[] args) {
// 4. 创建线程对象
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
// 5. 调用start方法开启线程
mt1.start();
mt2.start();
}
}
// 1. 编写一个类继承Thread
class MyThread extends Thread {
// 2. 重写fun方法
@Override
public void run() {
// 3. 将线程任务代码写在run方法中
for (int i = 1; i <= 200; i++) {
System.out.println("线程任务执行了" + i);
}
}
}
package com.itheima.thread;
public class ThreadDemo2 {
/*
Java程序默认是多线程的, 程序启动后默认会存在两条线程
1. 主线程
2. 垃圾回收线程
*/
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
for (int i = 1; i <= 2000; i++){
System.out.println("main线程执行了");
}
}
}
class Demo {
@Override
protected void finalize() throws Throwable {
System.out.println("垃圾被清理了");
}
}
1.2.2 实现Runnable接口
package com.itheima.thread;
public class ThreadDemo3 {
/*
开启线程的第二种方式: 实现Runnable接口
1. 编写一个类实现Runnable接口
2. 重写run方法
3. 将线程任务代码写在run方法中
4. 创建线程任务资源
5. 创建线程对象, 将资源传入
6. 使用线程对象调用start方法, 开启线程
*/
public static void main(String[] args) {
// 4. 创建线程任务资源
MyRunnable mr = new MyRunnable();
// 5. 创建线程对象, 将资源传入
Thread t = new Thread(mr);
// 6. 使用线程对象调用start方法, 开启线程
t.start();
for (int i = 1; i <=2000; i++){
System.out.println("main线程执行了");
}
}
}
// 1. 编写一个类实现Runnable接口
class MyRunnable implements Runnable {
// 2. 重写run方法
@Override
public void run() {
// 3. 将线程任务代码写在run方法中
for(int i = 1; i <=200 ; i++){
System.out.println("线程任务执行了" + i);
}
}
}
1.2.3 实现Callable接口
package com.itheima.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class ThreadDemo4 {
/*
开启线程的第三种方式: 实现Callable接口
1. 编写一个类实现Callable接口
2. 重写call方法
3. 将线程任务代码写在call方法中
4. 创建线程任务资源对象
5. 创建线程任务对象, 封装线程资源
6. 创建线程对象, 传入线程任务
7. 使用线程对象调用start开启线程
*/
public static void main(String[] args) throws Exception {
// 创建线程任务资源对象
MyCallable mc = new MyCallable();
// 创建线程任务对象, 封装线程资源
FutureTask<Integer> task1 = new FutureTask<>(mc);
FutureTask<Integer> task2 = new FutureTask<>(mc);
// 创建线程对象, 传入线程任务
Thread t1 = new Thread(task1);
Thread t2 = new Thread(task2);
// 使用线程对象调用start开启线程
t1.start();
t2.start();
Integer result1 = task1.get();
Integer result2 = task2.get();
System.out.println("task1获取到的结果为:" + result1);
System.out.println("task2获取到的结果为:" + result2);
}
}
// 1. 编写一个类实现Callable接口
class MyCallable implements Callable<Integer> {
// 2. 重写call方法
@Override
public Integer call() throws Exception {
// 3. 将线程任务代码写在call方法中
int sum = 0;
for (int i = 1; i<=100; i++){
sum += i;
System.out.println("sum=" + sum);
}
return sum;
}
}
2. Thread类的常见方法
2.1 返回/设置名字&获取当前线程对象方法
打开方式一: 继承Thread类
package com.itheima.method;
public class ThreadNameDemo1 {
/*
线程设置名字和获取名字
Thread类的方法:
public String getName() : 获取线程名字
public void setName() : 设置线程名字
public static Thread currentThread() : 获取当前线程的对象
*/
public static void main(String[] args) {
MyThread mt1 = new MyThread("A: ");
MyThread mt2 = new MyThread("B: ");
//mt1.setName("A: ");
//mt2.setName("B: ");
mt1.start();
mt2.start();
}
}
class MyThread extends Thread {
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 1; i <= 200; i++) {
System.out.println(super.getName() + "线程任务执行了" + i);
}
}
}
打开方式二: 实现Runnable接口
package com.itheima.method;
public class ThreadNameDemo2 {
/*
线程设置名字和获取名字
Thread类的方法:
public String getName() : 获取线程名字
public void setName() : 设置线程名字
public static Thread currentThread() : 获取当前线程的对象
*/
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t = new Thread(mr, "A: ");
t.start();
for (int i = 1; i <=2000; i++){
// 打印主线程名字
System.out.println(Thread.currentThread().getName() + "---" +"main线程执行了");
}
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
for(int i = 1; i <=200 ; i++){
// 打印当下线程名字
System.out.println(Thread.currentThread().getName() + "线程任务执行了" + i);
}
}
}
打开线程方式三: 实现Callable接口
package com.itheima.method;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class ThreadNameDemo3 {
/*
线程设置名字和获取名字
Thread类的方法:
public String getName() : 获取线程名字
public void setName() : 设置线程名字
public static Thread currentThread() : 获取当前线程的对象
*/
public static void main(String[] args) throws Exception {
MyCallable mc = new MyCallable();
FutureTask<Integer> task1 = new FutureTask<>(mc);
FutureTask<Integer> task2 = new FutureTask<>(mc);
Thread t1 = new Thread(task1,"线程A: ");
Thread t2 = new Thread(task2, "线程B: ");
t1.start();
t2.start();
Integer result1 = task1.get();
Integer result2 = task2.get();
System.out.println(t1.getName()+"task1获取到的结果为:" + result1);
System.out.println(t2.getName()+"task2获取到的结果为:" + result2);
}
}
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i<=100; i++){
sum += i;
System.out.println(Thread.currentThread().getName() + "sum=" + sum);
}
return sum;
}
}
2.2 休眠线程的方法
package com.itheima.method;
public class ThreadMethodDemo1 {
/*
休眠线程的方法:
public static void sleep(long time) : 让线程休眠指定的时间, 单位为毫秒
*/
public static void main(String[] args) throws InterruptedException {
for (int i = 5; i >= 1; i--){
System.out.println("倒计时"+ i + "秒");
Thread.sleep(1000);
}
}
}
2.3 线程优先级的方法
package com.itheima.method;
public class ThreadMethodDemo2 {
/*
线程优先级的方法:
public setPriority(int newPriority) : 设置线程优先级
public final int getPriority() : 获取线程优先级
*/
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 200; i++){
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
}, "线程A: ");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 200; i++){
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
}, "线程B: ");
t1.setPriority(1);
t2.setPriority(10);
System.out.println(t1.getPriority());
System.out.println(t2.getPriority());
t1.start();
t2.start();
}
}
2.4 设置守护线程的方法
package com.itheima.method;
public class ThreadMethodDemo3 {
/*
public final void setDaemon(boolean on) : 设置为守护线程, 守护线程跟着另一个线程的结束而结束.
*/
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <=20; i++){
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
}, "线程A: ");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <=200; i++){
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
}, "线程B: ");
t2.setDaemon(true);
t1.start();
t2.start();
}
}
3. 线程的安全和同步
3.1 线程安全之负号票
出现负号票原因:
33
3.2 线程安全之重复票
出现重复票原因(cpu在线程之间来回切):
解决方法-上锁:
3.3 线程安全与同步技术
3.3.1 同步代码块
package com.itheima.lock;
public class ThreadTest1 {
/*
需求: 某电影院目前正在上映国产大片, 共有100张票, 而它有3个窗口买票, 请设计一个程序模拟该电影票买票
- 多条线程共享操作同一份资源
同步代码块:
synchronized(锁对象) {
多条语句操作共享数据的代码
}
*/
public static void main(String[] args) {
TicketTask task = new TicketTask();
Thread t1 = new Thread(task, "窗口1");
Thread t2 = new Thread(task, "窗口2");
Thread t3 = new Thread(task, "窗口3");
t1.start();
t2.start();
t3.start();
}
}
// 方式一: 实现Runnable接口
class TicketTask implements Runnable {
private Object o = new Object();
private int tickets = 2000;
@Override
public void run() {
while (true) {
// 锁对象可以是任意对象o
synchronized (o){
if (tickets <= 0) {
break;
}
System.out.println(Thread.currentThread().getName() + "卖出了第" + tickets + "号票");
tickets--;
}
}
}
}
package com.itheima.lock;
public class ThreadTest2 {
/*
同步代码块:
synchronized(锁对象) {
多条语句操作共享数据的代码
}
*/
public static void main(String[] args) {
TicketTask2 t1 = new TicketTask2();
TicketTask2 t2 = new TicketTask2();
TicketTask2 t3 = new TicketTask2();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class TicketTask2 extends Thread {
// 加static确保资源开辟空间位置只有一个
private static int tickets = 2000;
private static Object o = new Object();
@Override
public void run() {
while (true) {
synchronized (o) {
if (tickets == 0) {
break;
}
System.out.println(getName() + "卖出了第" + tickets + "号票");
tickets--;
}
}
}
}
package com.itheima.lock;
public class ThreadTest3 {
/*
同步代码块:
synchronized(锁对象) {
多条语句操作共享数据的代码
}
*/
public static void main(String[] args) {
TicketTask2 t1 = new TicketTask2();
TicketTask2 t2 = new TicketTask2();
TicketTask2 t3 = new TicketTask2();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
// 方式二: 继承Thread类
class TicketTask3 extends Thread {
// 加static确保资源开辟空间位置只有一个
private static int tickets = 2000;
@Override
public void run() {
while (true) {
// 用类的字节码对象作为锁对象
synchronized (TicketTask3.class) {
if (tickets == 0) {
break;
}
System.out.println(getName() + "卖出了第" + tickets + "号票");
tickets--;
}
}
}
}
3.3.2 同步方法
3.3.3 Lock 锁
package com.itheima.lock;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadTest6 {
/*
Lock锁:
lock.lock();
...
lock.unlock();
*/
public static void main(String[] args) {
TicketTask6 task = new TicketTask6();
Thread t1 = new Thread(task,"窗口1");
Thread t2 = new Thread(task,"窗口2");
t1.start();
t2.start();
}
}
class TicketTask6 implements Runnable {
private int tickets = 100;
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock();
if (tickets == 0) {
break;
}
System.out.println(Thread.currentThread().getName() + "卖出了第" + tickets + "号票");
tickets--;
} finally {
lock.unlock();
}
}
}
}
3.3.4 死锁
4. 线程池
4.1 线程池介绍
package com.itheima.threadpool;
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
}
package com.itheima.threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo1 {
/*
public static ExecutorService newCachedThreadPool() : 创建一个没有上限的线程池
public static ExecutorService newFixedThreadPool(int nThreads) : 创建有上限的线程池
*/
public static void main(String[] args) {
// 1. 获取线程池对象
ExecutorService pool1 = Executors.newFixedThreadPool(3);
// 2. 提交任务
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
// 3. 销毁线程池
pool1.shutdown();
}
}
4.2 自定义线程
package com.itheima.threadpool;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolDemo2 {
/*
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor();
(核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);
参数一: 核心线程数量 不能小于0
参数二: 最大线程数 不能小于0, 最大数量 >= 核心线程数量
参数三: 空闲线程最大存活时间 不能小于0
参数四: 时间单位 用TimeUnit指定
参数五: 任务队列 不能为null
参数六: 创建线程工厂 不能为null
参数七: 任务的拒绝策略 不能为null
*/
public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(
3, //核心线程数量, 不能小于0
6, //最大线程数, 不能小于0, 最大数量 >= 核心线程数量
60, //空闲线程最大存活时间
TimeUnit.SECONDS, //时间单位
new ArrayBlockingQueue<>(3), //任务队列
Executors.defaultThreadFactory(), //创建线程工厂
new ThreadPoolExecutor.AbortPolicy() //任务的拒绝策略
);
}
}
4.3 最大并行数
package com.itheima.threadpool;
public class ThreadPoolDemo3 {
public static void main(String[] args) {
//向Java虚拟机返回可用处理器的数目
int count = Runtime.getRuntime().availableProcessors();
System.out.println(count);
}
}