线程问题

线程的入门

在了解线程之前,首先明白什么是进程。

什么是进程:

进程是指运行中的应用程序,每个进程都会有自己独立的地址空间(内存空间)比如:浏览器,编译器,系统任务管理器等等。操作系统会给该进程分配独立的地址空间,当用户再次点击浏览器时,就又启动一个进程。用户每启动一个进程,操作系统就会给该进程分配一个独立的内存空间。

什么是线程:

线程可以理解成进程的寄生,一个进程可以由多个线程。线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但他可与同属一个进程的其他线程共享所拥有的全部资源,一个线程可以创建和撤销另一个线程,线程可以并发执行。

线程有三种基本状态:就绪,阻塞,运行。

线程的理解:

1、线程是轻量级进程。

2、线程没有独立的地址空间

3、线程由进程创建(寄生)

4、一个进程可以拥有多个线程------>多线程

5、线程的生命周期:

  新建状态,就绪状态,运行状态,阻塞状态,死亡状态。

线程的应用场景:

1、我们熟悉的Tomcat服务器内部就是使用多线程:

上百个客户访问同一个web应用,Tomcat接入后都是把后续的处理扔给一个新的线程来处理,这个新的线程就是servlet程序,比如doGetdoPost

如果不采用多线程,上百个用户同时访问一个web应用的时候,只能使用串行处理,那样就不实际。

2、异步处理:

异步处理也使用多线程,比如task a和task b要并行处理。但是CPU是多核的话,就可以让一个CPU执行有一个线程,如果只有一个CPU的话,底层是按照分时复用的原则,各个线程按照时间获取CPU资源。

3、javaweb应用中很少用到多线程,因为在开发过程中,多线程被框架实现了,需要自己实现的很少。

4、用的比较多的时网络应用程序。

线程的安全问题:

为什么又线程安全问题?

 

当多个线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。

经典案例就是抢火车票的案例啦:

class ThreadTrain1 implements Runnable {

private int count = 100;

private static Object oj = new Object();

 

@Override

public void run() {

while (count > 0) {

try {

Thread.sleep(50);

} catch (Exception e) {

// TODO: handle exception

}

sale();

}

}

 

public void sale() {

// 前提 多线程进行使用、多个线程只能拿到一把锁。

// 保证只能让一个线程 在执行 缺点效率降低

// synchronized (oj) {

// if (count > 0) {

System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - count + 1) + "");

count--;

// }

// }

}

}

 

public class ThreadDemo {

public static void main(String[] args) {

ThreadTrain1 threadTrain1 = new ThreadTrain1();

Thread t1 = new Thread(threadTrain1, "①号窗口");

Thread t2 = new Thread(threadTrain1, "②号窗口");

t1.start();

t2.start();

}

}

运行结果:

可以看到一号跟二号出现抢到同一张票,这显然是不可用的

线程安全的解决办法:

使用同步synchronized或者使用锁(lock)

为什么使用多线程同步或者锁就能解决线程安全问题?

答:因为多线程可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程执行,同步/锁内的代码执行完之后,才能让其他线程进来执行,一个一个执行,这样就可以解决线程不安全问题。多个线程共享一个资源,不会收到其他线程的干扰。

多线程同步代码块案例:

private static Object oj = new Object();   

public void sale() {

// 前提 多线程进行使用、多个线程只能拿到一把锁。

// 保证只能让一个线程 在执行 缺点效率降低

 synchronized (oj) {

if (count > 0) {

System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - count + 1) + "票");

count--;

}

 }

}

也可以这样写:

public synchronized void sale() {

if (trainCount > 0) { 

try {

Thread.sleep(40);

} catch (Exception e) {

}

System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "张票.");

trainCount--;

}

}

 注意:使用同步代码块的时候一般都是使用当前类。静态方法时使用锁时当前类的字节码文件。

多线程死锁:

 简单理解:同步中嵌套同步,导致锁无法释放。

class ThreadTrain6 implements Runnable {

// 这是货票总票数,多个线程会同时共享资源

private int trainCount = 100;

public boolean flag = true;

private Object mutex = new Object();

 

@Override

public void run() {

if (flag) {

while (true) {

synchronized (mutex) {

// 锁(同步代码块)在什么时候释放? 代码执行完, 自动释放锁.

// 如果flag为true 先拿到 obj,在拿到this 锁、 才能执行。

// 如果flag为false先拿到this,在拿到obj锁,才能执行。

// 死锁解决办法:不要在同步中嵌套同步。

sale();

}

}

} else {

while (true) {

sale();

}

}

}

public synchronized void sale() {

synchronized (mutex) {

if (trainCount > 0) {

try {

Thread.sleep(40);

} catch (Exception e) {

 

}

System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "张票.");

trainCount--;

}

}

}

}

 

public class DeadlockThread {

 

public static void main(String[] args) throws InterruptedException {

 

ThreadTrain6 threadTrain = new ThreadTrain6(); // 定义 一个实例

Thread thread1 = new Thread(threadTrain, "一号窗口");

Thread thread2 = new Thread(threadTrain, "二号窗口");

thread1.start();

Thread.sleep(40);

threadTrain.flag = false;

thread2.start();

}

 

}

多线程的三大特性:

1、原子性

原子性是指不可分割,必须前后一致,比如去银行存取钱,A账户往B账户里转1000块钱,A账户里减去1000,B账户增加1000,前后不可以有分割。

2、可见性

多个线程访问同一个变量时,一个线程修改了该变量的值,其他线程能够立即看到修改的值

3、有序性

 程序执行的顺序按照代码的先后顺序执行。

Java内存模型:

共享内存模型指的就是Java内存模型(简称JMM)JMM决定一个线程对共享变量的写入时,对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。

 

转载于:https://www.cnblogs.com/InterfaceAOP/p/10223700.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实验目的 (1)掌握Windows系统提供的线程创建与撤销系统调用 (2)掌握Windows系统环境下线程的创建与撤销方法 2 实验准备知识 (1)线程创建 CreateThread()完成线程的创建。它在调用进程的地址空间上创建一个线程,执行指定的函数,并返回新建立线程的句柄。 原型: HANDLE CreateThread(   LPSECURITY_ATTRIBUTES lpThreadAttributes,   DWORD dwStackSize,   LPTHREAD_START_ROUTINE lpStartAddress,   LPVOID lpParameter,   DWORD dwCreationFlags,   LPDWORD lpThreadId);   参数说明:   lpThreadAttributes:指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,它被设为NULL,表示使用缺省值。   dwStackSize,线程堆栈大小,一般=0,在任何情况下,Windows根据需要动态延长堆栈的大小。   lpStartAddress,指向线程函数的指针,形式:@函数名,函数名称没有限制,但是必须以下列形式声明:   DWORD WINAPI ThreadProc (LPVOID pParam) ,格式不正确将无法调用成功。   lpParameter:向线程函数传递的参数,是一个指向结构的指针,不需传递参数时,为NULL。   dwCreationFlags :线程标志,可取值如下   CREATE_SUSPENDED: 创建一个挂起的线程   0 :创建后立即激活。   lpThreadId:保存新线程的id。   返回值:   函数成功,返回线程句柄;函数失败返回false。 (2)撤销线程 ExitThread()用于撤销当前线程 原型: VOID ExitThread( DWORD dwExitCode ); 参数说明: DwExitCode:指定线程返回码 返回值: 该函数没有返回值 用法举例: ExitThread(0); (3)挂起线程 Sleep()用于挂起当前正在执行的线程 原型: VOID Sleep( DWORD dwMilliseconds ); 参数说明: dwMilliseconds:指定挂起时间,单位为ms(毫秒)。 返回值: 该函数没有返回值。 (4)关闭句柄 函数CloseHandle()用于关闭已打开对象的句柄,其作用与释放动态申请的内存空间类似,这样可以释放系统资源,使进程安全运行。 原型: BOOL CloseHandle( HANDLE hObject ); 参数说明: hObject:已打开对象的句柄。 返回值: 成功,返回值为非0值;失败,则返回值为0.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值