一、线程基础内容
1、程序、进程与线程
1、程序:Program,是一个指令的集合
2、进程:Process,(正在执行中的程序)是一个静态的概念进程是程序的一次静态执行过程,占用特定地的地址空间
每个进程都是独立的,由3部分组成cpu,data,code
缺点:内存的浪费,cpu的负担3、线程:是进程中一个“单一的连续控制流程”/执行路径
线程又被成为轻量级进程
Threads run at the same time,independently of one another
一个进程可拥有多个并行的线程
一个进程中的线程共享相同的内存单元/内存地址空间->可以访问相同的变量和对象,而且他们从同一堆中分配对象->通信、数据交换、同步操作
由于线程间的通信是在同一地址空间上进行的,所以不需要额外的通信机制,这就使得通信更简便而且信息的传递速度也更快
2、线程的创建和启动
2.1、第一种方式
继承Thread类,重新run方法,调start(启动线程),每次运行相同的代码,出来的结果可能不一样,原因在于多线程谁先抢占资源无法进行认为控制
package com.msbline.threadPkg;
public class ThreadDemo extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++){
System.out.println(Thread.currentThread().getName() +"---"+ i);
}
}
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();
for (int i = 0; i < 10; i++){
System.out.println(Thread.currentThread().getName() +"==="+ i);
}
}
}
2.2、第二种方式
实现Runnable接口,重写run方法,创建Thread对象,将刚刚创建好的Runnable的子类实现作为Thread的构造参数,通过Trhead.start()进行启动
package com.msbline.threadPkg;
public class ThreadDemo02 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++){
System.out.println(Thread.currentThread().getName() +"---"+ i);
}
}
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
new Thread(threadDemo).start();
for (int i = 0; i < 10; i++){
System.out.println(Thread.currentThread().getName() +"==="+ i);
}
}
}
两种方式哪种使用更多
3、线程的生命周期
4、线程的代理设计模式
5、线程操作的相关方法
1、sleep 方法是属于 Thread 类中的,sleep 过程中线程不会释放锁,只会阻塞线程,让出cpu给其他线程,但是他的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态,可中断,sleep 给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会2、yield和 sleep 一样都是 Thread 类的方法,都是暂停当前正在执行的线程对象,不会释放资源锁,和 sleep 不同的是 yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。还有一点和 sleep 不同的是 yield 方法只能使同优先级或更高优先级的线程有执行的机会
3、join,等待调用join方法的线程结束之后,程序再继续执行,一般用于等待异步线程执行完结果之后才能继续运行的场景。例如:主线程创建并启动了子线程,如果子线程中药进行大量耗时运算计算某个数据值,而主线程要取得这个数据值才能运行,这时就要用到 join 方法了
4、wait 方法是属于 Object 类中的,wait 过程中线程会释放对象锁,只有当其他线程调用 notify 才能唤醒此线程。wait 使用时必须先获取对象锁,即必须在 synchronized 修饰的代码块中使用,那么相应的 notify 方法同样必须在 synchronized 修饰的代码块中使用,如果没有在synchronized 修饰的代码块中使用时运行时会抛出IllegalMonitorStateException的异常
二、线程同步
1、线程同步的必要性
多个线程访问同一个共享数据的时候,会出现数据安全问题,比如买票,两个线程同时对一张票进行操作,可能会导致重票的问题
2、线程同步的实现
2.1、同步代码块
synchronized(共享资源,共享对象,需要是Object的子类){具体执行的代码块}
public class TicketRunnable2 implements Runnable{
private int ticket = 5;
@Override
public void run() {
for(int i = 0; i<100; i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (this){
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"票");
}
}
}
}
public static void main(String[] args) {
TicketRunnable2 ticketRunnable = new TicketRunnable2();
Thread t1 = new Thread(ticketRunnable,"A");
Thread t2 = new Thread(ticketRunnable,"B");
Thread t3 = new Thread(ticketRunnable,"C");
Thread t4 = new Thread(ticketRunnable,"D");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
2.2、同步方法
public class TicketRunnable3 implements Runnable{
private int ticket = 5;
@Override
public void run() {
for(int i = 0; i<100; i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.sale();
}
}
/**
* 使用同步方法解决
*/
public synchronized void sale(){
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"票");
}
}
public static void main(String[] args) {
TicketRunnable3 ticketRunnable = new TicketRunnable3();
Thread t1 = new Thread(ticketRunnable,"A");
Thread t2 = new Thread(ticketRunnable,"B");
Thread t3 = new Thread(ticketRunnable,"C");
Thread t4 = new Thread(ticketRunnable,"D");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
3、死锁
4、线程同步小结
三、线程间通信
1、线程间通信的必要性
一般来说,每个线程自己完成自己的任务就可以了,但有时候,线程的处理会依赖另一个线程的数据,所以就需要线程间通信,来达到同步信息的效果。
2、线程间通信的实现
参考:https://blog.csdn.net/jisuanji12306/article/details/86363390
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。