一、什么是线程
-
线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。
——百度百科
二、实现线程的几种方式
- 继承Thread类重写run()方法
public class ThreadDemo {
public static void main(String[] args) {
MyThread myThread = new MyThread();
/*开启线程*/
myThread.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
// 写入自己的功能代码
}
}
- 实现Runnable接口
public class RunnableDemo {
public static void main(String[] args) {
/*通过实现Runnable接口开启多线程*/
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
/*通过匿名内部类实现*/
new Thread(()-> {
// 写入自己的功能代码
}).start();
new Thread(()-> System.out.println("线程4")).start();
new Thread(()-> System.out.println("线程5")).start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
// 写自己的功能代码
}
}
- 线程常用方法详解
-
获取当前正在执行此代码的线程信息Thread.currentThread()
public class MyThread {
public static void main(String[] args) {
/**
* ()->{
* // 获取当前线程信息
* Thread thread = Thread.currentThread();
* System.out.println(thread.toString());
*}
* 此处相当于传了一个实现了Runnable接口类的对象
* */
new Thread(()->{
Thread thread = Thread.currentThread();
System.out.println(thread.toString());
}).start();
}
}
-
判断线程是否处于活动状态Thread.currentThread().isAlive()
public class MyThread {
public static void main(String[] args) {
new Thread(()->{
System.out.println(Thread.currentThread().isAlive());
}).start();
}
}
-
使当前线程进行休眠Thread.sleep(5000);
public class MyThread {
/*HelloWorld需要等待5s才能被打印*/
public static void main(String[] args) {
new Thread(()->{
try {
/*线程休眠*/
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("HelloWorld");
}).start();
}
}
-
使当前线程释放锁wait()
-
唤醒其他在等待锁的线程(注意执行完notify()
方法,等待锁的线程不能马上获取到锁,得等待调用notify()
方法的线程执行完同步代码块或同步方法)notify()
三、实现锁的几种方式
- synchronized
-
锁定的是对象,而不是代码块,synchronized
是可以重入锁,当程序出现异常时会释放锁synchronized
public class MyThread { private static Object object = new Object(); public static void main(String[] args) { /*此处锁的是object对象*/ synchronized (object){ // 功能代码 } } /*此处锁定的是当前对象*/ public synchronized void m(){ } /*在没有new MyThread类对象时使用m1()方法,锁的是MyThread.class 对象*/ public static synchronized void m1(){ } /*可重人锁,当线程获取到了执行demo1方法的对象锁,当执行到demo2()时也能获取到对象锁*/ public synchronized void demo1(){ // 功能代码 demo2(); } public synchronized void demo2(){ } }
- synchronized底层原理(升级锁)
- 第一个线程进入的时候是偏向锁,所谓偏向锁就是锁对象会记录获取了锁对象的当前线程的id,如果锁没有释放锁,这个时候有另一个线程需要进入,就会用线程id对比一下是不是当前线程如果不是则获取不到锁。
- 当等待的线程变多就会升级为自旋锁(在用户态进行,在CPU中进行自旋)
- 进一步升级为重量级锁–OS锁(在内核态进行,不占用CPU)
- 拓展
- 线程少,占用锁时间短采用自旋锁,线程多占用锁时间长用系统锁
-
四、volatile注意看代码注释
- 防止指令重排序
public class Singleton {
/*
* 说明:
* Object o = new Object();
* 此条语句不是原子性操作,并且是分三步进行的
* 1、申请地址空间
* 2、初始化
* 3、将地址赋值给变量o
* 由于JVM存在指令重排机制一次1,2,3不一定会按顺序执行,如果在高并发情况下可能会有线程拿到还未进行初始化的对象
* */
private volatile Singleton instance = null;
private Singleton(){ }
public Singleton getInstance(){
if(instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
return instance;
}
}
- 保证被修饰变量线程间的可见性,底层是通过CPU的缓存一致性协议(MESI)完成的
public class MyThread extends Thread { /*此处不加关键字volatile线程thread1将一直在处在wile循环中,因为对主线程修改的flag不可见*/ private static volatile boolean flag = false; public void run() { while (!flag); } public static void main(String[] args) throws Exception { Thread thread1 = new MyThread(); thread1.start(); Thread.sleep(2000); flag = true; } }