构造方法
Java创建多线程示例
class MyThread extends Thread {
@Override
public void run() {
while (true) {
System.out.println("hello word!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Test1 {
public static void main(String[] args) throws InterruptedException {
MyThread thread = new MyThread();
thread.start();
while (true) {
System.out.println("hello main!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
while (true) {
System.out.println("Hello Runnable");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Test2 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread t = new Thread(myRunnable);
t.start();
}
}
public class Test3 {
public static void main(String[] args) {
Thread t = new Thread() {
@Override
public void run() {
while (true) {
System.out.println("Hello anonymous");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t.start();
}
}
public class Test4 {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("Hello anonymous runnable");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
}
}
public class Test5 {
public static void main(String[] args) {
Thread t = new Thread(()->{
while (true) {
System.out.println("Hello lambda");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
}
public class Test6 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<Integer> callable = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum += i;
}
return sum;
}
};
FutureTask<Integer> futureTask = new FutureTask<>(callable);
Thread t = new Thread(futureTask);
t.start();
Integer result = futureTask.get();
System.out.println(result);
}
}
public class Test7 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(4);
for (int i = 0; i < 100; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("Hello threadPool: executorService");
}
});
}
}
}
多线程的命名
run方法 和 start方法的区别
观察现象
得出结论
ID
这个是线程的身份标识 – 在JVM中给线程设定的标识
线程的不同身份标识:
不同身份标识的意义: 可以解耦合!
状态和优先级
是否是后台线程 – isDaemon()
是否存活 – isAlive()
Thread 对象, 对应的线程(系统内核中), 是否存活;
Thread 对象的生命周期, 并不是和系统中的线程完全一致的!
之前我们已经看到了如何通过覆写 run 方法创建一个线程对象,但线程对象被创建出来并不意味着线程就开始运行了。
终止线程的方法
public class Test02 {
private static boolean isQuit = false;
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (!isQuit) {
System.out.println("Hello world");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
Thread.sleep(3000);
isQuit = true;
System.out.println("把t线程终止了");
}
}
lambda表达式捕捉变量的理解 |
问题提出:
问题解决
如果这个变量想要修改,就不能进行变量捕获了
相比较之下,其它语言(JS)采取了更激进的设计 – 也有变量捕获,不是通过复制的方式实现,而是直接改变外部变量的生命周期从而保证 lambda 在执行的时候肯定能访问到外部的变量 – 此时JS的变量捕获就没有final的限制
public class Test03 {
public static void main(String[] args) throws InterruptedException {
Thread t =new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Hello world");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
});
t.start();
Thread.sleep(3000);
t.interrupt(); // 将 Thread 中断标志位设置为 true
System.out.println("把t线程终止了");
}
}
运行现象
join() 方法的参数
没有参数的是阻塞的等待线程结束, 就是让等待线程不在执行代码了, 不在使用CPU的资源了 – 但是死的策略不合适, 所以我们应该设定等待线程结束最大的时间 – 只要时间到, 不管来没来, 就不等了
join 能否被 interrupt 唤醒? --可以的-- 会自动清除中断标志位
【使用示例】
public class Test04 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
int cnt = 5;
while (cnt-- != 0) {
System.out.println("Hello join");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t thread end");
});
t.start();
t.join();
System.out.println("main thread end");
}
}
就绪状态、阻塞状态是系统设定的状态, Java对线程的状态的设定进行更进一步的细分.
线程状态是一个枚举类型 Thread.State
public static void main(String[] args) {
for(Thread.State x : Thread.State.values()) {
System.out.println(x);
}
}
解决线程安全的主要手段: 加锁, 将修改操作打包成原子性的
语法格式:
synchronized (一个对象) {
// 代码块
}
作用 :
内存可见性引起的线程安全问题
问题现象指出
while (!isQuit) {}
该执行代码本质上有两个指令:
volatile 解决内存可见性的问题
编译器的优化是一种玄学, 为了保证代码的逻辑, 不能完全依靠编译器的优化
Java 中的内存模型
wait
wait 在执行的时候, 会做三件事:
wait需要的注意事项 |
notify
wait 和 notify 的控制线程的执行顺序, 解决线程饥饿的问题
wait 和 sleep 之间的区别
sleep 是有一个明确的时间, 到达时间, 自然会被唤醒, 也能提前唤醒, 使用 interrupt 就可以
wait 默认是一个死等, 一直等到有其他线程 notify . wait 也能够被 interrupt 提前唤醒.
- notify是顺理成章的唤醒, 唤醒之后该线程还需要继续工作, 后续还会进入到 wait 状态
- interrupt 告知线程要结束了, 接下来线程就要进入到收尾工作了
wait 也有一个带有超时间的版本(和 join 类似)
因此, 协调多个线程之间阿德执行顺序, 当然还是优先考虑使用 wait notify, 而不是 sleep