1、创建线程:
Thread类、Runable接口、Callable带返回值的接口;在使用的时候尽量考虑使用接口实现,因为我们java是单继承多实现;
public class CreateThreadDemo {
//为何都是使用start()方法 而不是run()方法;因为start方法在创建线程,
// run()则是直接调用该方法并没有创建多线程
public static void main(String[] args) {
//1.继承Thread
Thread thread = new Thread() {
@Override
public void run() {
System.out.println("继承Thread");
super.run();
}
};
thread.start();
//2.实现runable接口
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("实现runable接口");
}
});
thread1.start();
//3.实现callable接口
ExecutorService service = Executors.newSingleThreadExecutor();
Future<String> future = service.submit(new Callable() {
@Override
public String call() throws Exception {
return "通过实现Callable接口";
}
});
try {
String result = future.get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
2、线程的状态:
基本概念:
NEW:调用start()初始状态
RUNNABLE:运行状态,将就绪和运行称作运行中
BLOCKED:阻塞,表示线程阻塞于锁
WAITING:等待状态表示线程进入等待状态,知道通知或中断
TIMED_WAITING:超时等待状态,在指定时间内自行返回
TERMINATED:终止状态,线程已执行完毕
线程状态案例:
package com.hezm.thread.day1.lifecycle;
import java.util.concurrent.TimeUnit;
public class ThreadStatusDemo {
/***
* 查看线程运行状态方法:
* 1、Recompile 或者run 运行一次程序
* 2、找到目录target-classes-com....下对应文件ThreadStatusDemo.class 右键 Open in Terminal
* 3、在console中运行jps 查看当前运行java进程
* 4、运行jstack 4044 jstack 进程号 查看线程堆栈日志
* blocked_thread_2 状态为 TIMED_WAITING
* blocked_thread_1 状态为 BLOCKED 锁被线程2抢占,线程1阻塞
* wating_thread 状态为 WAITING
* time_wating_thread 状态为 TIMED_WAITING
* @param args
*/
public static void main(String[] args) {
new Thread(()->{
try {
while (true){
TimeUnit.SECONDS.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"time_wating_thread").start();
new Thread(()->{
try {
while (true){
synchronized (ThreadStatusDemo.class){
//获得锁,然后wait;
ThreadStatusDemo.class.wait();
}
}
//InterruptedException线程可能异常就中断了;
} catch (InterruptedException e) {
e.printStackTrace();
}
},"waiting_Thread").start();
new Thread(new BlockedDemo(),"blocked_thread_1").start();
new Thread(new BlockedDemo(),"blocked_thread_2").start();
}
//阻塞线程
static class BlockedDemo extends Thread{
@Override
public void run() {
try {
while (true){
synchronized (ThreadStatusDemo.class){
TimeUnit.SECONDS.sleep(100);
}
}
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程状态转换图:
3、线程基本操作:
join()实现顺序执行:
package com.hezm.thread.day1.safe;
/**
* 实现线程的顺序执行
*/
public class JoinRule {
static int x = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
x=100;
});
t1.start();
t1.join();
//值一定为100,在join之后对线程内的操作可见;
System.out.println(x);
Thread t2 = new Thread(()->{
System.out.println("t1");
});
Thread t3 = new Thread(()->{
System.out.println("t2");
});
Thread t4 = new Thread(()->{
System.out.println("t3");
});
t2.start();
t2.join(); // 通过wait方法实现 直到线程被销毁的时候释放; 实现主线程阻塞等待线程执行完成;
t3.start();
t3.join();
t4.start();
t4.join();
}
}
线程的优雅中断1:interrupted,实现原理:通过一个volatile修饰的标志位来标识是否中断线程;
package com.hezm.thread.day1.stop;
import java.util.concurrent.TimeUnit;
public class InterruptThread {
private static int i = 0;
/***
* 线程的优雅中断;
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
//判断是否标记要中断线程: 默认值为false,true表示中断;
while (!Thread.currentThread().isInterrupted()){
i++;
System.out.println(i);
}
});
thread.start();
System.out.println(thread.isInterrupted());
TimeUnit.SECONDS.sleep(1);
thread.interrupt(); //将 isInterrupted 的值设置为true,标记要进行中断;
System.out.println(thread.isInterrupted());
//源码: private native void interrupt0();
//jvm方法: os :: interrupt(Thread thread)
// 有很多os,针对不同的操作系统有不同的实现;
}
}
线程复位:
package com.hezm.thread.day1.stop;
import java.util.concurrent.TimeUnit;
/**
* 线程复位:对中断的线程进行中断复位
* 方式1:Thread.interrupted()
* 方式2:InterruptedException; 复位的原因:复位标识我得到外界要求中断的信号,然后自己择机中断;
* 3,为什么所有和阻塞相关的方法都有 InterruptedException 异常?
* wait 和 sleep等在阻塞中间可能外部会强行关闭线程,当关闭线程的时候就会抛出 InterruptedException 异常
* 抛出异常以后就可以进行响应的处理;因此强行关闭,线程并没有死去;
*/
public class ThreadResetDemo {
//方式1: Thread.currentThread().isInterrupted()
// public static void main(String[] args) throws InterruptedException {
// Thread thread = new Thread(()->{
// //是否为中断? 默认值为false;
// while (true){
// if(Thread.currentThread().isInterrupted()){
// System.out.println("before:"+Thread.currentThread().isInterrupted());
// Thread.interrupted(); //复位回到初始状态
// System.out.println("after:"+Thread.currentThread().isInterrupted());
// }
// }
// });
//
// thread.start();
// TimeUnit.SECONDS.sleep(1);
// thread.interrupt();
// }
//方式2:InterruptedException,异常的复位
/**
* 使用场景:在异常被抓住的时候,我们可以对线程进一步处理;是抛出异常,中断程序或者其他都可以;就
*/
private static int i = 0;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
//是否为中断? 默认值为false;
while (!Thread.currentThread().isInterrupted()){
try {
System.out.println(System.currentTimeMillis() + "in Thread");
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
//中断一个处于阻塞状态的线程 join/wait/queue.take; 首先进行复位,然后中断线程;
System.out.println(System.currentTimeMillis() + "==c==" + Thread.currentThread().isInterrupted());
e.printStackTrace();
}
i++;
}
System.out.println("i:" + i);
});
// 即使主线程启动了TimeUnit.SECONDS.sleep(1) 也会导致thread的 InterruptedException 异常;
// 因此,休眠前需要将状态进行修改(thread.interrupt())才能体现 InterruptedException 对状态的的修改;
thread.start();
System.out.println(System.currentTimeMillis() + "==a==" + thread.isInterrupted());
thread.interrupt();
System.out.println(System.currentTimeMillis() + "==b==" + thread.isInterrupted());
TimeUnit.SECONDS.sleep(1);
}
}
可重入读写锁的使用:
package com.hezm.thread.day1.theory;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**可重入读写锁*/
public class ReentrantReadWriteLockDemo {
static ReentrantReadWriteLock writeLock = new ReentrantReadWriteLock(); //可重入读写锁;读不需要加锁。
static Map<String, Object> cacheMap = new HashMap<>();
static Lock read = writeLock.readLock();
static Lock write = writeLock.writeLock();
public static void main(String[] args) {
/**
* 适用于:读多写少的场景:
* 读->读可以共享 // Thread2:在get的时候, Thread1:线程在get的时候不会阻塞,可共享数据;
* 读->写互斥 // Thread2:在put的时候, Thread1:线程在get的时候会阻塞,等待写完才读,读到最新数据;
* 写->写互斥 // Thread2:在put的时候, Thread1:线程在put的时候会阻塞,等待写完才put;
*/
put("zhangsan","23岁");
get("zhangsan");
}
//Thread1:读锁:读的时候获取最新数据
public static final Object get(String key){
System.out.println("begin read deta:" + key);
read.lock();
try {
return cacheMap.get(key);
} finally {
read.unlock(); //防止死锁,一直不释放锁
}
}
//Thread2:写锁:写操作对读可见
public static final Object put(String key, Object value){
write.lock();
try {
System.out.println("begin write deta:" + key + "value" + value);
return cacheMap.put(key,value);
} finally {
write.unlock(); //防止死锁,一直不释放锁
}
}
}