文章目录
线程的创建和使用
多线程创建,方式一:继承Thread类
-
- 创建一个继承 Thread 的子类
- 重写 Thread 类中的 run() --> 此线程执行的操作申明在run() 中
- 创建 Thread 类的子类的对象
- 通过此对象调用 start()
public class ThreadTest extends Thread{
@Override
public void run() {
// 线程休眠 1秒
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
String odd = "";
for (int i = 1; i <= 100; ++ i) {
if (1 != (i & 1)) {
odd = odd + i + " ";
}
}
System.out.println("1~100的偶数有:" + odd);
}
public static void main(String[] args) {
ThreadTest t1 = new ThreadTest();
t1.start();
String even = "";
for (int i = 1; i <= 100; ++ i) {
if (1 == (i & 1)) {
even += i + " ";
}
}
System.out.println("1~100的奇数为:" + even);
}
}
-
Thread中的常用方法:
-
- start() : 启动当前线程; 调用当前线程的 run()
- run() : 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
- currentThread() : 静态方法,返回执行当前代码的线程
- getName() : 获取当前线程的名字
- setName() : 设置当前线程的名字
- yield() : 释放当前线程的执行权
- sleep(Long milltime) : 令当前活动线程在指定时间内放弃对CPU控制,使其他线程有机会被执行,时间到后重新排队。抛出异常InterruptedException异常
- join() : 在线程a中调用线程b的 join(),此时线程a就进入到阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态
- stop() : 强制线程生命周期结束,不推荐使用
- isAlive() : 判断当前线程是否存活
-
-
线程的调度
-
调度策略
-
- 时间片
- 抢占式:高优先级的线程抢占CPU
-
-
Java的调度方法
-
- 同优先级线程组成先进先出队列(先到先服务),使用时间策略
- 对高优先级,使用优先调度的抢占式策略
-
-
线程的优先级
- 线程的优先级是来改变线程被cpu调度的概率,不是说优先级高就一定被先调度,取决于cpu。
- MAX_PRIORITY: 10
- MIN_PRIORITY: 1
- NORM_PRIORITY: 5
-
涉及方法
-
getPriority(): 返回线程的优先值
-
setPriority(int newPriority): 改变线程的优先级
-
Thread t1 = new Thread(window, "窗口1"); t1.getPriority(); t1.setPriority(Thread.MAX_PRIORITY);
-
-
说明
- 线程创建时继承父线程的优先级
- 低优先级只是获得调度的概率低,并非一定是高优先级线程之后才被调用
-
多线程创建,方式二:实现Runnable接口
-
- 创建一个实现了Runnable接口的类
- 实现类去实现Runnable中的抽象方法: run()
- 创建实现类对象
- 将此对象作为参数传递到Thread类的构造器中,创建Thread类对象
- 通过Thread类的对象调用 start()
package 继承Thread;
public class ThreadTest extends Thread{
@Override
public void run() {
// 线程休眠 1秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String odd = "";
for (int i = 1; i <= 100; ++ i) {
if (1 != (i & 1)) {
odd = odd + i + " ";
}
}
System.out.println("1~100的偶数有:" + odd);
}
public static void main(String[] args) {
ThreadTest t1 = new ThreadTest();
t1.start();
String even = "";
for (int i = 1; i <= 100; ++ i) {
if (1 == (i & 1)) {
even += i + " ";
}
}
System.out.println("1~100的奇数为:" + even);
}
}
多个线程共享一个静态资源(无锁)
模拟买票,因为每个线程都有一个虚拟内存,当读到共享资源的时候,将共享资源复制到它虚拟内存内进行处理,然后将结构又赋值给当前的共享资源,这样会导致,当两个线程同时读到相同的票,让后造成重票问题, 当然如果没有出现网络延迟的话,或者处理速度非常快,一般发现不了。
package 实现Runnable;
public class WindowStore {
public void main(String[] args){
Window window = new Window();
new Thread(window).start();
new Thread(window).start();
new Thread(window).start();
}
}
class Window implements Runnable{
private int tickets = 100;
@Override
public void run() {
while (true) {
if (tickets > 0) {
try {
Thread.sleep(100); // 模拟网络延时
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 购买了第" + tickets-- + "票");
}
}
}
}
比较上面两种方式
开发中: 优先选择:实现Runnable接口的方式。
原因: 1. 实现的方式没有类的单继承性的局限性(java中类是不支持多继承,而且当前要使用多线程的类,可能需要继承它的父类,所以用实现方式更方便)。
2.实现的方式更适合用来处理多个线程共享数据的情况(实现方式,是将同一个对象,当成参数传入到多个线程中,保证了数据的唯一,二继承的方式必须声明多个对象)。
线程的分类
Java 中的线程分为两类:一种是 守护线程, 一种是 用户线程。
- 它们在几乎每个方面都是相同的,唯一的区别是判断JVM何时离开。
- 守护线程 是用来服务用户线程的,通过在 start() 方法前调用(thread.setDaemon(true))可以把一个用户线程变成一个守护线程。
- Java 垃圾回收是一个典型的守护线程。
- 若Jvm中都是守护线程,当前的jvm将退出。
- 形象理解:兔死狗烹,鸟尽弓藏。
创建多线程,方式三:实现Callable接口
使用步骤
- 创建一个实现的Callable的实现类
- 实现call()方法,将此线程需要执行的操作声明在call()中
- 创建Callable接口的实现类的对象
- 将此Callable接口的实现类对象作为参数,传递到FutureTask构造器中,创建FutureTask的对象
- 将FutureTask对象作为参数,传递到Thread类的构造器中,创建Thread对象,并start()
- 获取Callable中call方法的返回值
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
// 创建一个实现的**Callable**的实现类
public class NumTread implements Callable {
// 实现**call**()方法,将此线程需要执行的操作声明在**call**()中
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) sum += i;
}
return sum;
}
public static void main(String[] args) {
// 创建**Callable**接口的实现类的对象
NumTread numTread = new NumTread();
//将此**Callable**接口的实现类对象作为参数,传递到FutureTask构造器中,创建**FutureTask**的对象
FutureTask<Integer> futureTask = new FutureTask<Integer>(numTread);
//将FutureTask对象作为参数,传递到**Thread**类的构造器中,创建**Thread**对象,并**start**()
new Thread(futureTask).start();
try {
// get返回值就时Callable实现类的重写的对象。
//获取**Callable**中**call**方法的返回值
Integer sum = futureTask.get();
System.out.println(sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
- 与使用Runnable相比,Callable功能更强大些
- 相比于run()方法,可以有返回值
- call()方法可以抛出异常
- 支持泛型返回值
- 需要借助FutureTask类,比如获取返回结果
创建多线程,方式四:使用线程池
背景: 经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能的影响很大。
思路: 提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回线程池中。可以避免频繁的创建和销毁、实现重复利用。类似生活中的公共交通工具。
优点:
- 提高响应速度(减少了创建新线程的时间)
- 降低资源的消耗(重复利用线程池中的线程,不需要每次都创建)
- 便于线程管理
- corePoolSize: 设置线程池核心池的大小
- maximumPoolSize:设置最大的线程数
- keepAliveTime: 线程池中的线程没有任务时最多保持多少时间后会终止
使用步骤
-
提供指定数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);// 能存十个线程的线程池
-
执行指定的线程操作。需要提供实现的Runnable接口,或提供实现的Callable接口的实现类对象
// Runnable实现类的对象,使用execute service.execute(new NumberThread()); // Callable实现类的对象使用,submit service.submit(Callable callable);
-
关闭线程池
service.shutdown();
测试代码
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
class NumberThread implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
}
class NumberThread1 implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
if (i % 2 != 0) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
}
public class PoolThread {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
//体现线程池的管理
//service1.setCorePoolSize(10);
// 一个线程遍历100以内的偶数,一个遍历一百以内的奇数
service.execute(new NumberThread()); // 适用于Runnable
service.execute(new NumberThread1()); // 适用于Runnable
//service.submit();// 适用于Callable
service.shutdown();// 关闭线程池
}
}
创建多线程方式有4种