通过继承Thread类,可以创建一个新的线程类。子类需要重写Thread类的run()
方法,该方法包含线程的执行逻辑。然后,可以创建该线程类的实例并调用start()
方法启动线程。
public class MyThread extends Thread{
public void run(){
for(int i=0;i<10;i++){
System.out.println("Value:"+i);
}
}
public static void main(String args[]){
MyThread t1 = new MyThread();
t1.start();
}
}
通过实现Runnable接口,可以将线程的执行逻辑封装在一个实现了run()
方法的类中。然后,创建Thread类的实例时,将实现了Runnable接口的类的实例传递给Thread的构造方法。
public class MyRunnable implements Runnable{
public void run(){
for(int i=0;i<10;i++){
System.out.println("Value:"+i);
}
}
public static void main(String args[]){
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
t1.start();
}
}
实现Callable
接口是Java中另一种实现多线程的方式,相对于Runnable
接口,Callable
接口提供了更强大的功能和更灵活的线程控制。主要区别在于,Callable
接口允许线程执行任务后返回一个结果,并且可以抛出异常。
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class MyCallable implements Callable {
public Integer call(){
int sum=0;
for (int i=0;i<10;i++){
sum+=i;
}
return sum;
}
public static void main(String args[]){
Callable mc = new MyCallable();
// 将Callable实例包装成FutureTask
FutureTask ft = new FutureTask<>(mc);
// 创建线程并启动
Thread t1 = new Thread(ft);
t1.start();
try {
// 获取线程执行结果
Integer result = ft.get();
System.out.println("Sum from Callable: " + result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
继承Thread类:
实现Runnable接口:
如果需要在定义线程的同时扩展其他类的功能,或者需要在多个线程之间共享数据,推荐使用实现Runnable
接口的方式。
如果简单地需要定义一个线程,而不用担心单继承的限制,并且不需要共享数据,可以使用继承Thread
类的方式。
synchronized
是Java中用于实现同步的关键字,它可以应用于方法和代码块。同步是为了确保多个线程在访问共享资源时的安全性,防止并发访问导致的数据不一致或其他问题。下面是对synchronized
的详细介绍:
使用synchronized
修饰方法,确保在同一时间只有一个线程能够访问该方法。这种方式适用于整个方法需要被同步的情况。
public class MyClass {
private int count = 0;
// 同步方法
public synchronized void increment() {
count++;
}
}
使用synchronized
修饰代码块,可以选择性地对一部分代码进行同步,而不是整个方法。这样可以提高程序的性能,因为只有必要的部分受到同步保护。
public class MyClass {
private int count = 0;
private Object lock = new Object();
public void increment() {
// 同步代码块
synchronized (lock) {
count++;
}
}
}
实例级别的锁: 使用synchronized
修饰非静态方法时,锁是当前对象实例。不同实例的方法调用之间互不影响。
public synchronized void instanceMethod() {
// 同步方法
}
synchronized
修饰静态方法时,锁是当前类的Class对象。不同实例之间的静态方法调用之间也会受到同步保护。public static synchronized void staticMethod() {
// 同步静态方法
}
在使用synchronized
时,要注意避免死锁的情况。死锁发生在两个或多个线程相互等待对方释放锁的情况下。为了避免死锁,要确保线程获取锁的顺序是一致的。
创建一百个线程对num进行 ++ 操作 ,每个线程中都是一个死循环,判断跳出循环的条件是num==100w跳出,并打印本线程执行了多少次
public class IncrementThread1 extends Thread {
private static int num = 0;
private static final int TARGET = 1000000;
@Override
public void run() {
int count = 0;
while (num < TARGET) {
num++;
count++;
}
System.out.println(Thread.currentThread().getName() + " executed " + count + " times.");
}
public static void main(String[] args) {
final int THREAD_COUNT = 100;
IncrementThread1[] threads = new IncrementThread1[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i] = new IncrementThread1();
threads[i].start();
}
// 等待所有线程执行完毕
for (int i = 0; i < THREAD_COUNT; i++) {
try {
threads[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Final num value: " + num);
}
}
加上synchronized
public void run() {
int count = 0;
while (num < TARGET) {
synchronized (IncrementThread.class) {
if (num < TARGET) {
num++;
count++;
}
}
}
System.out.println(Thread.currentThread().getName() + " 执行了 " + count + " 次。");
}
死锁是多线程编程中一种常见的问题,它发生在两个或多个线程相互等待对方释放锁的情况下,导致程序无法继续执行。死锁通常是由于多个线程争夺资源时,每个线程都持有一部分资源并等待其他线程释放它所需要的资源,形成了相互等待的循环。
互斥条件: 至少有一个资源是被独占的,即只能由一个线程同时使用。
不可剥夺条件: 一个线程在持有资源的同时可以请求其他资源,而且不释放已经持有的资源。
请求与保持条件: 线程已经持有了一些资源,又在请求其他资源,此时不能抢占已经被其他线程持有的资源。
循环等待条件: 存在一个线程等待序列,其中每个线程都在等待下一个线程所持有的资源。
两个线程之间互相持有对方需要的锁,两个线程都持有了一把锁,接着都需要再持有一把锁(也是对方目前持有的锁)才可以执行任务
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TA extends Thread {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
synchronized (Main.object1) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("TA - 持有 O1 Lock");
synchronized (Main.object2) {
System.out.println("TA - 持有 O2 Lock");
Main.num++;
}
}
}
System.out.println("TA- end");
}
}
class RA implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
synchronized (Main.object2) {// synchronized 大括号范围之内 在线程级别都是一个原子性的操作
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("RA - 持有 O2 Lock");
synchronized (Main.object1) {
System.out.println("RA - 持有 O1 Lock");
Main.lock.lock();
Main.num++;
Main.lock.unlock();
}
}
}
System.out.println("RA- end");
}
}
class Main {
static int num = 0;
static Object object1 = new Object();
static Object object2 = new Object();
static Lock lock = new ReentrantLock();
public static void main(String[] args) {
// 实例化线程对象
TA ta = new TA();
RA ra = new RA();
ta.start();
Thread t1 = new Thread(ra);
t1.start();
// 等待线程执行完成
try {
ta.join(); // 阻塞方法直到线程执行完成
t1.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(num);
}
}
加锁顺序: 确保所有线程按照相同的顺序获得锁,以减少循环等待的可能性。
使用定时锁: 使用tryLock()
来尝试获取锁,设置超时时间,如果超过时间仍未获得锁,就释放已经持有的锁。
避免嵌套锁: 尽量避免在持有锁的情况下再去获取其他锁,以减少死锁的概率。
定期检测: 定期检测系统中是否存在死锁,并采取相应的措施,如强制终止其中一个线程。