一、开启线程的三种方式
第一种,通过继承Thread类创建线程类
public class ExtendThread extends Thread {
private int i;
public static void main(String[] args) {
for(int j = 0;j < 50;j++) {
//调用Thread类的currentThread()方法获取当前线程
System.out.println(Thread.currentThread().getName() + " " + j);
if(j == 10) {
//创建并启动第一个线程
new ExtendThread().start();
//创建并启动第二个线程
new ExtendThread().start();
}
}
}
public void run() {
for(;i < 100;i++) {
//当通过继承Thread类的方式实现多线程时,可以直接使用this获取当前执行的线程
System.out.println(this.getName() + " " + i);
}
}
}
第二种,通过实现Runnable接口创建线程类
public class ImpRunnable implements Runnable {
private int i;
@Override
public void run() {
for(;i < 50;i++) {
//当线程类实现Runnable接口时,要获取当前线程对象只有通过Thread.currentThread()获取
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
public static void main(String[] args) {
for(int j = 0;j < 30;j++) {
System.out.println(Thread.currentThread().getName() + " " + j);
if(j == 10) {
ImpRunnable thread_target = new ImpRunnable();
//通过new Thread(target,name)的方式创建线程
new Thread(thread_target,"线程1").start();
new Thread(thread_target,"线程2").start();
}
}
}
}
第三种,通过Callable和Future接口创建线程
public class ThirdThreadImp {
public static void main(String[] args) {
//这里call()方法的重写是采用lambda表达式,没有新建一个Callable接口的实现类
FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)()->{
int i = 0;
for(;i < 50;i++) {
System.out.println(Thread.currentThread().getName() +
" 的线程执行体内的循环变量i的值为:" + i);
}
//call()方法的返回值
return i;
});
for(int j = 0;j < 50;j++) {
System.out.println(Thread.currentThread().getName() +
" 大循环的循环变量j的值为:" + j);
if(j == 20) {
new Thread(task,"有返回值的线程").start();
}
}
try {
System.out.println("子线程的返回值:" + task.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}
二、三种创建方式对比
通过继承Thread类实现多线程:
优点:
1、实现起来简单,而且要获取当前线程,无需调用Thread.currentThread()方法,直接使用this即可获取当前线程;
缺点:
1、线程类已经继承Thread类了,就不能再继承其他类;
2、多个线程不能共享同一份资源(如前面分析的成员变量 i );
通过实现Runnable接口或者Callable接口实现多线程:
优点:
1、线程类只是实现了接口,还可以继承其他类;
2、多个线程可以使用同一个target对象,适合多个线程处理同一份资源的情况。
缺点:
1、通过这种方式实现多线程,相较于第一类方式,编程较复杂;
2、要访问当前线程,必须调用Thread.currentThread()方法。
三、线程的五种状态
1、创建状态
2、就绪状态
3、运行状态
4、阻塞状态
5、死亡状态
守护线程
线程分为用户线程和守护线程
虚拟机必须确保用户线程执行完毕
虚拟机不必等待守护线程执行完毕,如:日志、内存监控、垃圾回收
四、同步方法
synchronized方法和synhronized同步块
synchronized方法控制着对对象
的访问,每个对象对应着一把锁,需要方法返回才释放锁
synhronized同步块
synhronized (Obj) {}
Obj称为同步监视器
Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
同步方法中无需制定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身
锁Lock
定义锁private final ReentrantLock lock = new ReentrantLock();
加锁:lock.lock();
解锁:lock.unlock();
推荐使用try{}finally{}
的方式进行加锁解锁
synchronized与Lock的对比
- Lock是显式锁(手动开启和关闭),synchronized是隐式锁,出了作用域会自动释放
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- 使用Lock锁,JVM将花费较少时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
- 优先使用顺序
Lock -> 同步代码块 -> 同步方法
五、线程协作
生产者消费者
网友评论