多线程基础
- 线程的启动,暂停,停止
- 线程的优先级
- 线程的安全性
什么是多线程,线程与进程
- 线程:线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
- 进程:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础
Java 实现多线程
- 继承Thread类,重写run方法
- 实现Runnable接口
使用第一种局限在于不可多继承,需要多继承可以使用第二种方式。
Thread本质就是实现Runnable接口。
public
class Thread implements Runnable {}
以下是通过第一种方式完成。
public class ThreadApp {
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
System.out.println("执行结束");
}
}
我的线程
public class MyThread extends Thread {
@Override
public void run() {
super.run();
System.out.println("我的线程...");
}
}
分析线程信息
public class ThreadApp2 {
public static void main(String[] args) {
System.out.println("s-----s");
for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
System.out.println("1-----1");
Thread.sleep(500000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
输入jstack -l 34646
,推荐使用jmc
可视化分析信息。
下载地址:https://www.oracle.com/java/technologies/javase/products-jmc8-downloads.html
关于使用,远程授权等方式,请查看博客。
MacOS安装jmc注意,启动报错,请在Info.plist添加下面的路径(下面2句)
<key>Eclipse</key>
<array>
<string>-keyring</string>
<string>~/.eclipse_keyring</string>
<string>-vm</string>
<string>/Library/Java/JavaVirtualMachines/jdk1.8.0_281.jdk/Contents/Home/jre</string>
</array>
运行程序后,查看分析器。
实现Runnable
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Runnable ----");
}
}
运行
public class ThreadApp3 {
public static void main(String[] args) {
Runnable mr = new MyRunnable();
Thread thread = new Thread(mr);
thread.start();
System.out.println("结束");
}
}
多线程变量安全问题
场景安排:
3名售货员出售5件货物。
- 数据不共享,他们每个人都5件货物
- 数据共享,卖的是同5件货物
代码模拟如下:
public class MyThread3 extends Thread {
private int count = 5;
public MyThread3(String name) {
super();
setName(name);
}
@Override
public void run() {
super.run();
while (count > 0) {
count--;
System.out.println("通过" + currentThread().getName() + "计算,count=" + count);
}
}
}
运行
public class ThreadApp5 {
public static void main(String[] args) {
MyThread3 mt1 = new MyThread3("A");
MyThread3 mt2 = new MyThread3("B");
MyThread3 mt3 = new MyThread3("C");
mt1.start();
mt2.start();
mt3.start();
System.out.println("结束");
}
}
结果
通过B计算,count=4
通过A计算,count=4
通过A计算,count=3
通过C计算,count=4
通过C计算,count=3
通过C计算,count=2
结束
通过C计算,count=1
通过A计算,count=2
通过B计算,count=3
通过A计算,count=1
通过A计算,count=0
通过C计算,count=0
通过B计算,count=2
通过B计算,count=1
通过B计算,count=0
Process finished with exit code 0
可以看出每个线程数据不共享,自己计算自己的,那么通过数据共享呢?
public class ThreadApp6 {
public static void main(String[] args) {
MyThread4 myThread = new MyThread4();
Thread mt1 = new Thread(myThread, "A");
Thread mt2 = new Thread(myThread, "B");
Thread mt3 = new Thread(myThread, "C");
mt1.start();
mt2.start();
mt3.start();
System.out.println("结束");
}
}
通过B计算,count=2
通过B计算,count=1
通过B计算,count=0
通过A计算,count=2
通过C计算,count=2
发现数据出现了重复减少,这样就造成了数据安全问题,即非线程安全。通过添加synchronized
实现线程排队。
结束
通过A计算,count=4
通过A计算,count=3
通过A计算,count=2
通过A计算,count=1
通过A计算,count=0
停止线程
- interrupt+异常停止
- interrupt+return停止
不可使用stop
暂停和恢复线程
- wait()
- notify()
public static void main(String[] args) {
new Thread(() -> {
long bt = System.currentTimeMillis();
int count = 0;
for (int i = 0; i < 500000; i++) {
Thread.yield();
count = count + (i + 1);
}
long et = System.currentTimeMillis();
System.out.println("用时:" + (et - bt));
}).start();
}
Thread.yield() 用法:放弃当前cpu,让其他任务去占用。
线程的优先级
可以通过setPriority
完成设置,1-10,但是优先级还是具有随机性。
守护线程
当线程只剩下守护线程的时候,JVM就会退出。守护线程最典型的应用就是 GC (垃圾回收器)。
通过设置setDaemon(true)
设置守护线程。