目录
方式一:继承 java.lang.Thread 类(线程子类)
方式二:实现 java.lang.Runnable 接口(线程执行类)
方式三:实现 java.util.concurrent.Callable 接口,允许子线程返回结果、抛出异常
一 、进程与线程的区别
进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位
当我们打开任务管理器时 里面的所有任务都是进程 而一个进程中至少包含一个线程
查看jvm进程由哪些线程组成
// 获取 Java 线程管理对象 ThreadMXBean
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
// 不需要获取的锁监视器 lockedMonitor 和 synchronizer 信息
// 仅获取线程和线程信息
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
// 遍历线程信息,仅打印线程 ID 和线程名称信息
for (ThreadInfo threadInfo : threadInfos) {
System.out.println("[" + threadInfo.getThreadId() + "] " + threadInfo.getThreadName());
}
这是我用idea查看结果
值得一提的时线程 [ 6 ] Monitor Ctrl - Break 是idea独有的线程(Run模式)
- [ 1 ] main 是主线程
- [ 2 ] Reference Handler 是清除 reference 线程
- [ 3 ] Finalizer 是调用对象 finalize 方法的线程
- [ 4 ] Signal Dispatcher 是分发处理给 JVM 信号的线程
- [ 5 ] Attach Listener 是添加事件监听器
二、线程的创建方式
四种方式,但其实都是基于Thread类
方式一:继承 java.lang.Thread 类(线程子类)
public class Main {
// 主线程main
public static void main(String[] args) {
//创建并启动子线程
SubThread s1 =new SubThread("线程1");
SubThread s2 =new SubThread("线程2");
SubThread s3 =new SubThread("线程3");
s1.start();
s2.start();
s3.start();
}
}
// 线程子类
class SubThread extends Thread{
public SubThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i <10 ; i++) {
System.out.println(this.getName()+":"+i);
}
}
}
方式二:实现 java.lang.Runnable 接口(线程执行类)
模拟同时通过JavaMail发送三分邮件(当然,并未有实际发送代码,在我写的第一篇博客中有详细代码,可以去了解。)
public class Main {
public static void main(String[] args) {
EmailTask emailTask =new EmailTask();
Thread t1 =new Thread(emailTask,"线程1");
Thread t2 =new Thread(emailTask,"线程2");
Thread t3 =new Thread(emailTask,"线程3");
t1.start();
t2.start();
t3.start();
}
}
class EmailTask extends Task implements Runnable{
@Override
public void run() {
Thread thread = Thread.currentThread();
String name = thread.getName();
System.out.println(name+":使用JavaMail技术,通过smtp协议发送邮件!");
}
@Override
public void execute() {
execute();
}
}
abstract class Task{
public abstract void execute();
}
方式三:实现 java.util.concurrent.Callable 接口,允许子线程返回结果、抛出异常
前面两种方式都没有返回值,如果有需要返回值的业务可以使用当前方式创建线程(当前例子是想要获得0-1000的累加和,但分为三次线程同时累加后,将三次的累加和求和即可。)
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("开始分别获取。。。。。");
//Callable
SumCalcTask s1 =new SumCalcTask(0,500);
SumCalcTask s2 =new SumCalcTask(501,800);
SumCalcTask s3 =new SumCalcTask(801,1000);
//Callable -->FutureTask(Runnable接口实现类)
FutureTask<Integer> futureTask1 = new FutureTask<Integer>(s1);
FutureTask<Integer> futureTask2 = new FutureTask<Integer>(s2);
FutureTask<Integer> futureTask3 = new FutureTask<Integer>(s3);
Thread t1 =new Thread(futureTask1);
Thread t2 =new Thread(futureTask2);
Thread t3 =new Thread(futureTask3);
t1.start();
t2.start();
t3.start();
System.out.println("汇总各自结果。。。。");
System.out.println("最终计算结果:"+ new Integer(futureTask1.get()+futureTask2.get()+futureTask3.get()));
}
}
class SumCalcTask implements Callable<Integer>{
private int begin,end;
public SumCalcTask(int begin,int end){
this.begin=begin;
this.end=end;
}
@Override
public Integer call() throws Exception {
int total = 0 ;
for (int i = begin; i <=end ; i++) {
total+=i;
}
System.out.println(Thread.currentThread().getName()+":计算完成!!");
return total;
}
}
方式四:线程池
此方式可以一次性创建10个线程,但同时也最多只能有10个线程同时进行,当有多的任务时,需等待,其中有线程空闲。(该例子是模拟投票,一次最多只能10个用户投票,只有投票结束后才能有新的用户投票,当然最多也只能10个用户。)该线程池的创建方法并不推荐,他只能创建固定大小的线程池。并不能设置其他参数。后续会补充推荐的创建线程池方法。
public static void main(String[] args) {
// 创建固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
while (true){
// 提交多个执行任务至线程池,并执行
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"投票!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
这有一个同时用四种方法创建四个线程的案例
// StringBuilder sb =new StringBuilder();
StringBuffer sb =new StringBuffer();
Thread t1 = new Thread(){
@Override
public void run() {
for (char c ='A';c<='E';c++){
sb.append(c);
}
}
};
Thread t2 =new Thread(new Runnable() {
@Override
public void run() {
for (char i = 'a'; i <='e' ; i++) {
sb.append(i);
}
}
});
Thread t3 =new Thread(new FutureTask<String>(new Callable<String>() {
@Override
public String call() throws Exception {
for (int i = 1; i <=5 ; i++) {
sb.append(i);
}
return sb.toString();
}
}));
ExecutorService executorService = Executors.newFixedThreadPool(5);
executorService.execute(new Runnable() {
@Override
public void run() {
char[] array ={'!','@','#','$','%'};
for (int i = 0; i <array.length ; i++) {
sb.append(array[i]);
}
}
});
executorService.shutdown();
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
System.out.println(sb);
三 、线程的状态
在Java程序中,一个线程对象通过调用start()方法启动线程,并且在线程获取CPU时,自动执行run()方法。run()方法执行完毕,代表线程的生命周期结束。
在整个线程的生命周期中,线程的状态有以下6种:
- New:新建状态,新创建的线程,此时尚未调用start()方法;
- Runnable:运行状态,运行中的线程,已经调用start()方法,线程正在或即将执行run()方法;
- Blocked:阻塞状态,运行中的线程,在等待竞争锁时,被阻塞,暂不执行;
- Waiting:等待状态,运行中的线程,因为sleep()join()等方法调用,进入等待;
- Timed Waiting:计时等待状态,运行中的线程,因为执行sleep(等待毫秒值)join(等待毫秒值)等方法,进入计时等待;
- Terminated:终止状态,线程已终止,因为run()方法执行完毕。
当线程启动后,它可以在Runnable、Blocked、Waiting和Timed Waiting这几个状态之间切换,直到最后变成Terminated状态,线程终止
线程终止的原因有:
- 线程正常终止:run()方法执行到return语句返回
- 线程意外终止:run()方法因为未捕获的异常导致线程终止
- 对某个线程的Thread实例调用stop()方法强制终止(宇宙超级无敌强烈不推荐)
四、窗口抢票案例
这个案例主要关键是需要加synchronized锁
因为如果在三个窗口同时抢票的话
会出现同一时间卖出两张或者三张票通过不同窗口
而导致的问题是可能卖出三张票 票池中却只减少了1票
而synchronized 的作用是 同一时间只能有一个线程访问票池
当有线程人竞争到锁 其他线程只能堵塞
public class Main {
public static void main(String[] args) {
TickThread tickThread =new TickThread(20);
new Thread(tickThread,"窗口1").start();
new Thread(tickThread,"窗口2").start();
new Thread(tickThread,"窗口3").start();
}
}
class TickThread implements Runnable{
//当前门票数
private int tickCount;
public TickThread(int tickCount) {
this.tickCount = tickCount;
}
private final Object obj =new Object();
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"准备开始卖票喽!!");
//线程竞争cpu执行权(this锁)
synchronized (obj){
while (true){
if (tickCount<=0){
System.out.println(Thread.currentThread().getName()+"已经没有票了!");
return;
}else {
System.out.println(Thread.currentThread().getName()+"卖出去了1张票,剩余"+ --tickCount+"张票!");
try {
//持有当前this锁的对象进入等待 1000毫秒
obj.wait(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public int getTickCount() {
return tickCount;
}
public void setTickCount(int tickCount) {
this.tickCount = tickCount;
}
}