1.相关概念
1.1程序,进程与线程
程序(Program):为完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码,静态对象。
进程 (Process) :进程是操作系统中执行的程序的实例。它是系统资源分配的基本单位,包括内存空间、文件描述符等。每个进程都有自己的地址空间,进程间的通信相对复杂,通常需要使用进程间通信 (IPC) 机制,如管道、消息队列等。
线程 (Thread) :线程是进程中的一个执行单元,是程序执行的最小单位。一个进程可以包含多个线程,这些线程共享进程的资源,如内存和文件描述符。线程之间的通信相对简单,因为它们共享同一进程的地址空间。
1.2查看线程与进程
Java提供了Thread
类来管理和查看线程。你可以使用以下方法获取当前线程的状态:
public class ThreadExample {
public static void main(String[] args) {
// 获取当前线程
Thread currentThread = Thread.currentThread();
// 打印线程名称
System.out.println("Thread Name: " + currentThread.getName());
// 打印线程优先级
System.out.println("Thread Priority: " + currentThread.getPriority());
// 打印线程状态
System.out.println("Thread State: " + currentThread.getState());
}
}
你可以使用Thread.getAllStackTraces()
方法来获取所有线程的堆栈跟踪信息:
import java.util.Map;
public class AllThreadsExample {
public static void main(String[] args) {
// 获取所有线程的堆栈跟踪
Map<Thread, StackTraceElement[]> allThreads = Thread.getAllStackTraces();
// 遍历所有线程
for (Map.Entry<Thread, StackTraceElement[]> entry : allThreads.entrySet()) {
Thread thread = entry.getKey();
StackTraceElement[] stackTrace = entry.getValue();
// 打印线程名称和状态
System.out.println("Thread Name: " + thread.getName() + ", State: " + thread.getState());
}
}
}
一个进程中包含多个线程
1.3线程调度
线程调度是操作系统或Java虚拟机(JVM)管理多个线程如何分配CPU时间的过程。以下是几个关键点:
-
线程优先级:Java允许为线程设置优先级(1到10),影响调度顺序,但不保证执行顺序。
-
时间片轮转:操作系统通常使用时间片轮转算法,给每个线程分配一定的CPU时间。
-
线程状态:线程有多种状态,包括新建、就绪、运行、阻塞和死亡,影响调度行为。
-
同步机制:使用
synchronized
或Lock
等工具确保对共享资源的安全访问,可能导致线程阻塞。 -
Thread.sleep()
:使当前线程暂停一段时间,让其他线程执行,但不释放锁。 -
Thread.yield()
:提示调度器当前线程愿意放弃CPU使用权,允许其他同级别或高优先级线程执行。 -
最佳实践:合理设计线程模型,避免过多依赖优先级和过度同步。
1.4多线程程序的优点
-
并发执行:多线程可以同时执行多个任务,提高程序的整体性能和响应能力,特别是在多核处理器上。
-
资源共享:线程之间可以共享内存和资源,减少资源消耗,相比于进程更轻量。
-
响应性:在用户界面应用中,多线程可以在后台执行耗时任务,保持界面的响应性。
-
提高吞吐量:对于需要长时间运行的任务(如网络请求或I/O操作),使用多线程可以提高系统的吞吐量。
-
更好的利用系统资源:多线程可以更有效地利用CPU和其他系统资源,减少空闲时间。
-
简化程序结构:在某些情况下,多线程可以使程序结构更清晰,例如将不同的功能模块分配给不同的线程。
-
灵活性:多线程编程允许动态调整线程的数量,以适应不同的负载和需求。
-
实现异步处理:通过使用线程,程序可以实现异步处理,提高效率,特别是在网络通信和文件处理等场景。
1.5并行与并发
并行:指的是多个任务在同一时间点上真正同时执行。通常需要多核或多处理器系统。
并发:指的是多个任务在同一时间段内执行,但不一定是同时。任务可能会交替进行,系统在不同任务之间切换。
2.创建和启动线程
2.1方式一:继承Thread类
package thread; public class thread extends Thread{ //1.创建一个继承于Thread的子类 //2.重写Thread中的run()方法 ----->将此线程要执行的操作声明在此方法体中 @Override public void run() { System.out.println("线程开始运行了!"); } public static void main(String[] args) { //3.创建当前Thread的子类的对象 thread t1=new thread(); //4.通过对象调用start方法 t1.start(); } }
2.2方式二实现Runnable接口
package runnable; public class Runnable implements java.lang.Runnable { //1.创建一个类实现Runnable接口 //2.重写接口中的run()方法 @Override public void run() { System.out.println("方法!"); } public static void main(String[] args) { // //3.创建当前实现类的对象 // Runnable r1=new Runnable(); // //4.将此对象作为参数传递到Thread构造器中,创建Thread类的实例 // Thread t1=new Thread(r1); // //5.通过Thread类的实例,调用start方法。 // t1.start(); /* new Thread(new Runnable(){ @Override public void run() { for (int i = 1; i <= 100; i++) { if (i%2==0){ System.out.println(Thread.currentThread().getName()+i); } } } }).start();*/ new Thread(new java.lang.Runnable() { @Override public void run() { for (int i = 1; i <= 100; i++) { if (i%2!=0){ System.out.println(Thread.currentThread().getName()+i); } } } }).start(); } }
3.线程的常用方法和生命周期
3.1线程的常用结构
3.2线程中的常用方法
3.3线程的优先级
3.4线程的生命周期
3.4.1JDK1.5之前:
3.4.2JDK1.5之后:
4.线程安全问题及解决
5.死锁
5.1如何看待死锁?
不同的线程分别占用对方需要的资源不放弃,都在等对方放弃自己需要的同步资源,就形成了死锁。
我们编写程序时,避免出现死锁。
5.2诱发死锁的原因
5.3如何避免死锁
6.Lock锁
6.1步骤
class Window extends Thread { static int sticks = 100; //创建lock实例,需要确保多个线程共用一个lock实例需要考虑将此对象声明为static final private static final ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true) { //执行lock方法,锁定对共享资源的共用 try { lock.lock(); if (sticks > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(Thread.currentThread().getName() + "正在买票,还剩下" + sticks + "张票"); sticks--; } if (sticks == 0) { return; } } finally { //unlock()释放对共享数据的锁定 lock.unlock(); } } } } public class exer1 { public static void main(String[] args) { Window t1 = new Window(); t1.setName("窗口1"); Window t2 = new Window(); t2.setName("窗口2"); Window t3 = new Window(); t3.setName("窗口3"); t1.start(); t2.start(); t3.start(); } }
6.2 synchronized同步的方式与lock的对比
7.线程的通信
7.1线程间通信的理解
当需要多个线程来完成一个共同的任务时,并且希望他们有规律的执行,那么多线程之间就需要一些通讯机制,可以协助他们的工作。即等待唤醒机制 。
7.2等待唤醒机制
class PrintNumber implements Runnable{ private int i=1; @Override public void run() { while (true){ synchronized (this) { notify(); if (i<=100){ System.out.println(Thread.currentThread().getName()+":"+i); i++; try { wait();//线程一旦进入该方法,就进入等待状态,同时,会释放对同步监视器的调用 } catch (InterruptedException e) { e.printStackTrace(); } }else { break; } } } } } public class exer2 { public static void main(String[] args) { PrintNumber p=new PrintNumber(); Thread t1=new Thread(p); t1.setName("线程1"); Thread t2=new Thread(p); t2.setName("线程2"); t1.start(); t2.start(); } }
7.2.1注意点
7.2.2sleep和wait的区别
相同点:sleep和wait方法都可以使线程进入阻塞状态。
不同点:wait只能声明在同步代码块或同步方法中,而sleep任何位置都可声明。
sleep不会释放对同步监视的调用,而wait会释放对同步监视器的调用。
wait方法到达指定时间自动结束阻塞状态 或 通过被notify唤醒,结束阻塞。
sleep方法到达指定时间自动结束阻塞。
8.JDK5.0之后新增的两种多线程创建方式
8. 1多线程创建方式三:实现Callable结口
class NumThread implements Callable { //实现call()方法,将此线程需要执行的操作声明在call()中 @Override public Object call() throws Exception { int sum = 0; for (int j = 1; j < 100; j++) { if (j % 2 == 0) { System.out.print(j + " "); sum += j; } } return sum; } } public class CallableTest { public static void main(String[] args) { //创建Callable接口实现类的对象 NumThread numThread = new NumThread(); //将此对象作为参数传递到FutureTask构造器中,创建FutureTask的对象 FutureTask futureTask = new FutureTask<>(numThread); //将此对象作为参数传递到Thread构造器中,创建Thread的对象 Thread t1 = new Thread(futureTask); t1.start(); try { int num = (Integer) futureTask.get(); System.out.println(); System.out.println(num); } catch (InterruptedException e) { throw new RuntimeException(e); } catch (ExecutionException e) { throw new RuntimeException(e); } } }
8.2多线程创建方式四:线程池
问题:如果并发的线程数量很多,并且每一个线程执行任务的时间很短,这样频繁创建线程就会大大降低系统的效率,线程的创建和销毁都需要时间。
线程池的思路:如果提前创建好多个线程,放入线程池中,使用时直接获取,用完放回池中,可以避免频繁创建销毁线程,实现重复利用。
标签:Java,Thread,void,线程,new,多线程,public From: https://blog.csdn.net/2302_78998188/article/details/143306830class Number 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 Number1 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 ExecutorTest { public static void main(String[] args) { //1.提供指定线程数量的线程池 ExecutorService executorService= Executors.newFixedThreadPool(10); ThreadPoolExecutor threadPoolExecutor=(ThreadPoolExecutor) executorService; //设置线程池的属性 threadPoolExecutor.setMaximumPoolSize(50);//设置线程池中线程数的上限 //2.执行指定的线程的操作,需要提供实现Runnable接口或Callable接口实现类的对象 threadPoolExecutor.execute(new Number());//适合用于Runnable threadPoolExecutor.execute(new Number1());//适合用于Runnable //threadPoolExecutor.submit(Callable callable);//适合用于Callable //3.关闭线程池 threadPoolExecutor.shutdown(); } }