1、线程介绍
2、创建并启动线程
2.1 继承Thread类
2.2 实现Runnable接口
2.3 实现Callable接口
3、线程的生命周期
3.1 生命周期
new ----start—>Runnable----->Running ---->blocked---->terminated
3.2 start方法源码解析
public class TempleteMethod {
public static void main(String[] args) {
TempleteMethod t1 = new TempleteMethod(){
@Override
protected void wrapPrint(String message) {
System.out.println(" message : " + message );
}
};
t1.print("hello ");
}
public final void print(String message){
System.out.println("***********");
wrapPrint(message);
System.out.println("***********");
}
protected void wrapPrint(String message){
System.out.println(message);
}
}
此例中:print方法类似于Thread中的start方法
4、Thread-构造函数
4.1 构造函数:Thread()
1、默认有一个线程名,以“Thread-”开头,从0开始计数
2、若没有重写run方法,则不会调用任何东西
3、如果构造线程对象时未传入ThreadGroup,此时Thread会获得父线程的ThreadGroup作为该线程的ThreadGroup,此时子线程和父线程出现在同一个ThreadGroup中
4.2 ThreadGroup
ThreadGroup tg = Thread.currentThread().getThreadGroup();
Thread[] th1 = new Thread[tg.activeCount()];
tg.enumerate(th1);
Arrays.asList(th1).forEach(System.out::println);
4.3 StackSize
1、构建Thread的时候传入stackSize代表着该线程占用的stack大小
2、如果没有指定stackSize的大小,默认是0,0代表着会忽略该参数,该参数会被JNI函数去使用,该参数在一些平台有效,在有些平台无效
5、Thread-API
5.1 守护线程
public class ThreadDaemon {
public static void main(String[] args) {
Thread thread = new Thread(()->{
try {
while (true){
System.out.println(" sleep --- ");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.setDaemon(true);
thread.start();
System.out.println(thread.isDaemon()); // 是否为守护线程
System.out.println(" main - thread ");
}
}
主线程生命周期结束,守护线程也结束
5.2 线程优先级
System.out.println(thread.getPriority()); //5
5.3 合并线程(join)
可以想象成插队(必须放在.start()之后)
thread.join(100); //可以设置时间
Thread.currentThread().join(); // 自己等自己 死等
5.4 中断(interrupt、wait)
thread.isInterrupted()
thread.interrupted()
5.5 采用优雅的方法结束线程生命周期
1、使用标志位
public class ThreadStop {
public static void main(String[] args) throws InterruptedException {
ThreadTest thread = new ThreadTest();
thread.start();
Thread.sleep(1000);
thread.shutdown();
}
}
class ThreadTest extends Thread {
private boolean flag = true;
@Override
public void run(){
int i = 0;
while (flag) {
System.out.println(" i : " + i ++ );
}
}
public void shutdown(){
this.flag = false;
}
}
2、捕获InterruptionException异常,直接break
3、Thread.interrupted
5.6 暴力结束线程实战
1、定义为守护线程
5.7 sleep和wait的区别
1、sleep是Thread的方法,wait是所有object的方法
2、sleep不会释放这个锁,而wait会释放锁并且会加入 object等待锁队列中
3、使用wait必须使用synchronized
4、sleep不需要被唤醒,但是wait需要
6、同步、并发、synchronize,死锁
6.1 synchronized
class Ticket implements Runnable{
private final Object object =new Object();
@Override
public void run() {
while (true) {
synchronized (object) {
if (true) {}
}
}
}
}
6.2 同步方法、同步代码块
1、同步代码块
class Ticket implements Runnable{
private final Object object =new Object();
@Override
public void run() {
while (true) {
synchronized (object) {
if (true) {}
}
}
}
}
2、同步方法(就是锁this)
public synchronized void buy(){
if (flag<= num) {
System.out.println( Thread.currentThread().getName() +" : "+ flag);
flag++;
}
}
6.3 this锁
1、代码实例:
public class ThisSync {
public static void main(String[] args) {
ThisTest thisTest = new ThisTest();
new Thread("T1") {
@Override
public void run(){
try {
thisTest.fun1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
new Thread("T2") {
@Override
public void run(){
try {
thisTest.fun2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
}
class ThisTest{
public synchronized void fun1() throws InterruptedException {
Thread.sleep(100);
System.out.println("fun1 ---- ");
}
public synchronized void fun2() throws InterruptedException {
// Thread.sleep(100);
System.out.println("fun2 ---- ");
}
}
当 给fun2()加锁时,main方法中按顺序执行
当不给fun2()加锁时,一般先执行完fun2
2、结论
锁方法就是锁 This
6.4 静态代码块加的锁就是 class锁
1、给静态代码块被jvm保证执行一次,对他加锁是否有意义?
没有意义,jvm保证静态代码块执行一次,保证了其的安全性
2、代码实例
static {
synchronized (ThisTest.class) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized static void fun3(){
try {
Thread.sleep(1000);
System.out.println("fun 3 ---- ");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
6.5 死锁
6.6 显示的锁Lock
7、线程之间的通信
7.1 线程之间未通信
public class ProduceConsumeV1 {
public static void main(String[] args) {
ProduceConsumeV1 p1 = new ProduceConsumeV1();
new Thread("t1") {
@Override
public void run(){
p1.produce();
}
}.start();
new Thread("t2") {
@Override
public void run(){
p1.consume();
}
}.start();
}
private int index = 0;
final Object object = new Object();
public void produce(){
synchronized (object) {
while (true) {
// if (index < 50)
System.out.println("pro -- " + index ++ );
}
}
}
public void consume(){
synchronized (object) {
while (true) {
// if (index >0)
System.out.println("con -- " + index -- );
}
}
}
}
7.2 线程之间的通信(通知:notify,等待:wait)
public class ProduceConsumeV2 {
public static void main(String[] args) {
ProduceConsumeV2 p2 = new ProduceConsumeV2();
new Thread("t1"){
@Override
public void run(){
try {
while (true){
p2.produce();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
new Thread("t2"){
@Override
public void run(){
try {
while (true){
p2.consume();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
private int index = 0;
final Object object = new Object();
private boolean flag = false;
public void produce() throws InterruptedException {
synchronized (object) {
if (flag) {
object.wait();
} else {
index++;
System.out.println("pro : " + index );
object.notify();
flag = true;
}
}
}
public void consume() throws InterruptedException {
synchronized (object) {
if (flag) {
System.out.println("com : " + index-- );
object.notify();
flag = false;
} else {
object.wait();
}
}
}
}
7.3 上述代码存在的问题
1、多个线程同时调用.produce和.consume时会出现错误(程序会假死)
2、问题分析:notify时,不知道notify的是哪一个具体的线程
3、问题解决:使用notifyAll()
public class ProduceConsumeV3 {
public static void main(String[] args) {
ProduceConsumeV3 p3 = new ProduceConsumeV3();
new Thread("push1"){
@Override
public void run(){
while (true){
p3.push();
}
}
}.start();
new Thread("pop1"){
@Override
public void run(){
while (true){
p3.pop();
}
}
}.start();
new Thread("pop2"){
@Override
public void run(){
while (true){
p3.pop();
}
}
}.start();
new Thread("push2"){
@Override
public void run(){
while (true){
p3.push();
}
}
}.start();
}
private int index = 0;
final Object object = new Object();
private boolean flag = false;
public void push(){
synchronized (object) {
while (flag) {
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
index ++ ;
System.out.println( Thread.currentThread().getName() + " push : " + index);
object.notifyAll();
flag = true;
}
}
public void pop(){
synchronized (object) {
while (!flag) {
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " pop : " + index );
// index -- ;
object.notifyAll();
flag = false;
}
}
}
8、一些方法
8.1 setUncaughtExceptionHandler
public class CatchException {
public static void main(String[] args) {
int a = 0;
int b = 10;
Thread thread = new Thread(()->{
try {
Thread.sleep(1_000);
int c = b/a;
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.setUncaughtExceptionHandler((t, e) -> System.out.println(e)); // java.lang.ArithmeticException: / by zero
thread.start();
}
}
8.2 方法追溯(Thread.currentThread().getStackTrace())
9、ThreadGroup
9.1 创建
ThreadGroup t1 = new ThreadGroup("tg1");
Thread thread = new Thread(t1,"hel") {
@Override
public void run(){
System.out.println(getThreadGroup().getName());// tg1
System.out.println(getThreadGroup().getParent()); //java.lang.ThreadGroup[name=main,maxpri=10]
}
};
thread.start();
ThreadGroup t2 = new ThreadGroup(t1,"tg2");
Thread thread1 = new Thread(t2,"hel") {
@Override
public void run(){
System.out.println(getThreadGroup().getName()); // tg2
System.out.println(getThreadGroup().getParent()); //java.lang.ThreadGroup[name=tg1,maxpri=10]
}
};
thread1.start();
9.2 常用 API
1、activeAccount()
2、activeGroupAccount()
10、线程池
10.1 线程池的原理
1、任务队列(调度)
2、拒绝策略(抛出异常,直接丢弃,阻塞,临时队列)
3、初始化值(init、active、max)
10.2 线程池实现
11、其他
11.1 ThreadLocal
- ThreadLocal的作用
他可以解决多线程的数据安全的问题
他可以给当前线程关联一个数据(可以是普通变量,也可以是对象,也可以是数组,集合)
- ThreadLocal的特点
ThreadLocal可以为当前线程关联一个数据(他可以像Map一样存取数据,key为当前线程)
每一个ThreadLocal对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个ThreadLocal对象实例
每一个ThreadLocal对象实例定义的时候,一般都是static类型
ThreadLocal中保存的数据,在线程销毁之后,会由JVM虚拟机自动释放