目录
一、创建线程方式一:继承Thread创建
1、优点:简单直观
继承 Thread 类并重写 run() 方法,可以使线程的创建和管理相对简单。
可以直接调用 start() 方法来启动线程。
2、缺点
Java 不支持多继承,因此如果已经继承了其他类,就无法再通过继承 Thread 类来创建线程。
继承 Thread 类的方式将线程和任务逻辑绑定在一起(耦合),不利于代码的组织和复用。
3、示例代码
public static void main(String[] args) {
TestThread thread1 = new TestThread();
TestThread thread2 = new TestThread();
thread1.start();
thread2.start();
}
public static class TestThread extends Thread{
@Override
public void run() {
super.run();
for(int count = 0; count < 5; count++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-"+count);
}
}
}
二、创建线程方式二:实现Runnable接口
1、优点:支持多继承、任务逻辑与线程逻辑分离、适合线程池等场景
由于 Java 中的类可以实现多个接口,因此使用 Runnable 接口可以避免继承 Thread 类的单继承限制。
使用 Runnable 接口,你可以将任务逻辑独立出来,便于任务的复用和组织。
线程池中通常使用 Runnable 接口来表示要执行的任务。
2、缺点
使用时需要借助 Thread 类来创建线程,稍显繁琐一些。
3、示例代码
public static void main(String[] args) {
TestRunnable runnable1 = new TestRunnable();
TestRunnable runnable2 = new TestRunnable();
Thread thread1 = new Thread(runnable1, "线程1");
Thread thread2 = new Thread(runnable2, "线程2");
thread1.start();
thread2.start();
}
public static class TestRunnable implements Runnable {
@Override
public void run() {
for (int count = 0; count < 5; count++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-" + count);
}
}
}
三、线程状态
线程可以具有不同的状态,表示线程在执行过程中的不同阶段和状态。
(1)New(新建)
当创建一个线程对象但尚未调用 start() 方法时,线程处于新建状态。在这个状态下,线程已经被创建,但尚未分配系统资源或开始执行。
(2)Runnable(可运行/就绪)
一旦调用了线程对象的 start() 方法,线程进入可运行状态。在这个状态下,线程已经分配了系统资源,可以被调度为运行状态,但实际上可能还没有开始执行,因为线程调度器可能会决定何时运行该线程。
(3)Running(运行)
当线程被线程调度器选中并开始执行时,线程处于运行状态。在这个状态下,线程的代码正在执行。
(4)Blocked(阻塞)
线程在某些情况下可能会被阻塞,暂时停止执行。当线程在等待某些条件满足时,比如等待获取一个锁或等待某个事件发生,就会进入阻塞状态。一旦条件满足,线程将从阻塞状态转移到可运行状态,等待线程调度器分配执行时间。
(5)Waiting(等待)
当线程被要求等待某些特定条件时,它会进入等待状态。这可以通过调用 Object 类的 wait() 方法或类似的等待方法来实现。在等待状态下,线程会释放它持有的锁,并等待其他线程通过 notify() 或 notifyAll() 方法来唤醒它。
(6)Timed Waiting(定时等待)
类似于等待状态,但在等待一段时间后会自动唤醒。这可以通过调用带有超时参数的等待方法来实现,如 wait(long timeout) 方法。
(7)Terminated(终止)
当线程的 run() 方法执行完毕或因异常而终止时,线程将进入终止状态。在终止状态下,线程不再执行任何代码。
四、线程调度
1、判断线程是否活动 isAlive()
判断一个线程是否还存活(即是否还在运行中)。返回 true 表示线程还在运行,返回 false 表示线程已经终止。
1、优先级 setPriority(int priority)
线程调度器会根据线程的优先级来确定哪个线程应该先执行。优先级范围是从 1 到 10,其中 1 是最低优先级,10 是最高优先级。默认值为5。
2、休眠 sleep(long millis)
使当前线程进入指定的毫秒数的休眠状态。在休眠期间,线程不会执行任何操作,可以用来模拟等待时间或者为其他线程让出执行时间。
3、等待和协作 join()
在一个线程内部调用另一个线程的 join() 方法,会等待另一个线程执行完毕后再继续执行当前线程。这个方法通常用于实现线程的等待和协作,比如等待子线程完成。
4、让出CPU执行时间 yield()
调用 yield() 方法会让当前线程主动让出 CPU 执行时间,让线程调度器决定下一个执行哪个线程。这是一种提高线程协作的方式,不过它并不保证一定会生效,具体取决于线程调度器的实现。
5、中断 interrupt()
向目标线程发送一个中断信号,用于请求目标线程停止执行。被中断的线程可以在适当的时候检查自己是否被中断,并作出响应。
测试代码:
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println("线程名称:"+thread.getName());
thread.setName("my-thread");
System.out.println("线程名称:"+thread.getName());
TestThread thread1 = new TestThread();
TestThread thread2 = new TestThread();
thread1.setName("线程11");
thread2.setName("线程22");
thread2.setPriority(10);//设置优先级
thread1.start();
thread2.start();
//thread1.isAlive(); //判断线程1是否还存活
try {
// 让主线程行等待 线程1 和 线程2 执行完成
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static class TestThread extends Thread{
@Override
public void run() {
super.run();
Thread.yield(); //让当前线程主动让出 CPU 执行时间
for(int count = 0; count < 100; count++) {
try {
Thread.sleep(1);//休眠1毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-"+count);
}
}
}
五、线程同步
synchronized 是 Java 中用于实现线程同步的关键字,它可以应用于方法或代码块。synchronized 的主要作用是确保在多线程环境中对共享资源的访问是线程安全的,防止多个线程同时访问和修改共享资源而导致的并发问题。
需要注意的是,过度使用 synchronized 可能会导致性能问题,因为它会引入线程之间的竞争和上下文切换开销。因此,在使用 synchronized 时,需要谨慎考虑并发性能问题,确保只在必要的情况下使用它,同时考虑使用更轻量级的同步机制,如 java.util.concurrent 包中提供的并发工具。
测试代码:
public static void main(String[] args) {
TestRunnable runnable = new TestRunnable();
Thread thread1 = new Thread(runnable, "线程1");
Thread thread2 = new Thread(runnable, "线程2");
thread1.start();
thread2.start();
}
public static class TestRunnable implements Runnable {
private int order = 0;
@Override
public void run() {
for (int count = 0; count < 10; count++) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//this.Test1(); //不加synchronized,输出序号不按顺序增加
//this.Test2(); //在方法上加synchronized,来同步
this.Test3(); //在代码块上加synchronized,来同步
}
}
private void Test1() {
order++;
System.out.println(Thread.currentThread().getName() + " 执行=" + order);
}
private synchronized void Test2() {
order++;
System.out.println(Thread.currentThread().getName() + " 执行=" + order);
}
private void Test3() {
synchronized (this) {
order++;
System.out.println(Thread.currentThread().getName() + " 执行=" + order);
}
}
}
六、常用的线程安全类
1、字符串处理类StringBuffer。
2、集合Hashtable、Vector,但是在新版中已过时,推荐使用ConcurrentHashMap、CopyOnWriteArrayList 来代替。
3、想要使用线程安全的集合类尽量使用java.util.concurrent包下的所有类。
有关集合使用相关问题请参考Java 基础之集合