一.回顾以前学过的有关线程的基本概念:
1.什么是线程?
线程是计算机中程序中独立运行的单位,每个java程序程序被启动时,java虚拟机都会创建一个主线程即(main函数)来使程序运行、即我们经常用到的main()函数,不管是在java还是c语言中都可以被理解成是一个线程。
2.线程和进程的区别?
进程:通俗来讲可以和一个程序的概念画上等号,一个程序就是一个进程。
线程:如上面所说是程序中一个独立运行的单位,也便是进程中的一部分,一个进程可以包含很多个线程。
多个进程的内部数据和状态是完全独立的,而一个进程中的多个线程是共享一块内存空间和一组系统资源的,在程序内部可以互相调用,而进程间的通信大多都要通过网络实现。
3.线程的创建
1.写一个java主函数(我觉得这个也是线程创建的一种方式
2.继承java.lang.Thread类
3.实现 java.lang.Runnable接口
4.匿名内部类的形式创建
以上几种方法这边就不在细说,详细请见博客中的线程总结(1)。
4.线程的状态
这个很多资料上都说法不一,有的说三种、有的说四种、也有五种的,要比较完整的话我觉得是五种。
1.创建状态:在创建了线程对象,但没有调用start()方法。
2.就绪状态:调用了start()方法后,就进入了就绪状态,但此时线程调度程序并没有把线程设定为当前线程,即run()方法还没开始运行。或者在线程从等待或者休眠中醒来,此刻线程也处于是就绪状态。
3.运行状态:线程调度程序将线程设定为当前线程,线程开始运行,即run()方法开始运行。
4.等待(阻塞)状态:当线程运行的时候被暂停。如sleep()等方法的调用是运行出现了阻塞的情况。
5.死亡状态:线程run()方法运行结束,该线程就进入死亡状态了。
以上是对线程一些基本知识点的回顾,下面讲的才是此篇博客的重点。
二。线程同步问题的产生、
1.为什么会产生线程同步的问题?
记得我第一次接触到线程同步问题是在听设计模式中的单例模式中碰到的。这里简单说一下单例模式:顾名思义,就是只能有一个例子,即某个类对象中只能产生一个对象。这样的类要怎么去编写?
当时用的是一种叫做懒汉式的方法,代码如下:
public static Student getStudent() {
if (stu == null) {
stu = new Student();
}
return stu;
}
分析:当student对象不为null时我们就new一个student对象,这个做法貌似看起来可行,但这样是否万无一失呢?
是的,在单线程中这样的单例模式是可行的,但多线程中是不可行的,当两个线程同时去访问这个判断条件的时候,或者说在student对象还没有被创建的时候有多个线程同时进入到了这个判断条件,那么这个条件显然是成立的,此时的student对象为null那么多个线程调用这个方法的时候也就产生了多个student对象,已经违反了单例模式的原则。
所以从上面的例子我们可以看出产生线程同步的问题的原因:就是多个线程之间去共享一个资源。如果线程之间没有共享的资源,同步问题也就不存在。
线程同步的问题还有很多的例子:比如对一个全局变量的处理,就拿整形数据i来讲,
在多个线程中同时对这个数据去处理,比如在一个线程中对它进行i++,在另外一个线程中进行i--,那么最后i的值将是无法预料的。因为1.你不知道那个线程先对i处理,这是我们无法控制的,如上面所讲线程运行时受线程调度程序控制的,并不能简单地理解为我们先start了哪一个线程那个线程就先运行。2.你不知道那个线程运行的快,两个线程之间的调用顺序。所以i的结构也就无法预料。这些都是线程的同步问题类似的例子还有很多。
2.如何解决线程同步问题?
就拿上面那个单例模式产生的线程问题来讲,我们可以这么想,要是使这个判断方法不能同时被两个线程或者多个线程同时访问,那么问题是不是就解决了。
那么该如何做到?Java线程正是提供这种机制的。用Synchronized(同步的意思)关键字。有以下两种做法来用这个关键字。
第一种方法:直接在方法前加入synchronized关键字public static synchronized Student getStudent(){
if(stu==null){
stu=new Student();
}
return stu;
}
第二种方法:
public static synchronized Student getStu() {
synchronized (Student.class) {
stu = new Student();
return stu;
}
}
这样就使这个方法或者是某些代码语句在某个时刻只能被一个线程访问
3.线程中的等待/通知(wait/notify)
线程消费模式:
有这样一个场景,一个手机工厂要生产手机,公司规定只有当库存为0的时候,工厂才能在生产手机,而消费者来买手机当手机公司库存里面有手机的时候他就可以买。以下代码实现。/**
* 手机类
* @author Administrator
*
*/
public class Phone {
private String type;
public Phone(String type){
this.type=type;
}
public String toString(){
return type;
}
}
import java.util.List;
public class Producer extends Thread{
//手机库存list
private List<Phone> list;
private int count=0;
public Producer(List<Phone> list){
this.list=list;
}
public void run(){
while(true){
try {
sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(list.size()==0){
Phone phone=new Phone("phone"+count);
System.out.println("工厂生产了手机"+phone);
list.add(phone);
count++;
}
}
}
}
import java.util.List;
/**
* 消费者类
* @author Administrator
*
*/
public class Customer extends Thread{
//手机库存list
private List<Phone> list;
public Customer(List<Phone> list){
this.list=list;
}
public void run(){
while(true){
System.out.println("消费者在访问");
try {
sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} if(list.size()>0){
Phone phone=list.remove(0);
System.out.println("消费者消费了手机"+phone);
}
}
}
}
/**
* 主函数测试
* @author Administrator
*
*/
public class Mian {
/**
* @param args
*/
public static void main(String[] args) {
List<Phone> list=new LinkedList<Phone>();
//创建一个生产线程对象并start
Producer producer=new Producer(list);
producer.start();
//创建一个消费者线程并start
Customer customer=new Customer(list);
customer.start();
}
}
这个代码可以这样解释:手机工厂和消费者一直在看手机库存,不停地看,工厂发现库里没有手机了就生产一台,消费者看都有手机了就买了一台,这样的做法很显然可以满足上面的需求,但这样消耗内存资源太大了,需要两方同时不停地去查看。从打印中也可以看出来。
我们会有这样的想法,当工厂生产出了一台手机的时候就去通知消费者,消费者就来买手机,买了手机后,消费者也去通知工厂说手机库存没了,此时工厂在去生产手机,除了接收到通知的其他时间工厂和消费者都可以休息。即等待wait,这样资料消耗就减少很多了。这就是非常典型的线程生产消费模型synchronized(list){
if(list.size()==0){
Phone phone=new Phone("phone"+count);
System.out.println("工厂生产了手机"+phone);
list.add(phone);
count++;
//生产完通知消费者
list.notify();
}
try {
//等待消费者通知
list.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
synchronized(list){
//消费者进入等待
try {
list.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(list.size()>0){
Phone phone=list.remove(0);
System.out.println("消费者消费了手机"+phone);
//消费者通知生产这已经消费了
list.notify();
}
}
这样从打印中可以看出,当执行wait()的时候,就阻塞了,知道有notify()的通知时候,阻塞才解除。生产者和消费者之间的访问都大大减少,每次都是有效地访问。
注意点:可能都注意到了关键字synchronized括号后面的参数了。这个参数可以这样去理解:它是去绑定不同线程之间的一把锁。两个线程之间的交互是必须用同一把锁的。当wait的时候即相当于这把锁被锁起来了,要去解锁必须是去解这个锁而不是去解另外的锁,即notify也必须要是这个对象去调用。
举个例子:当你上厕所的时候里面有人了,他把门锁着,那么你只能等,当它出来了,把这个锁给解开了,你才能进厕所。要是别的测锁锁开了,你也是进不了这个门的。所以锁和解是必须要对同一个对象而言的。至于什么对象,在能实现这个功能上来讲是没有什么区别的。但是从其他方面,如占用的内存等等,运行的效率来讲就有区别了。这边就不在细究。
分享到:
相关推荐
C#.net 同步异步 SOCKET 通讯和多线程总结 本文旨在总结 C#.net 中的同步异步 SOCKET 通讯和多线程编程,涵盖服务端和客户端的实现细节。 一、服务端实现 服务端使用 System.Net 和 System.Net.Sockets 命名空间...
2. 调用`native`方法`start0()`,这是一个本地方法,用于实际启动线程。`native`关键字表明该方法的实现位于Java虚拟机之外,通常是操作系统层面的线程调度。 ### 总结 Java多线程提供了强大的并发处理能力,...
### Java多线程编程总结 #### 一、Java线程:概念与原理 - **操作系统中线程和进程的概念** 当前的操作系统通常都是多任务操作系统,多线程是一种实现多任务的方式之一。在操作系统层面,进程指的是内存中运行的...
【Windows多线程总结】 Windows操作系统提供了一套完整的API来支持多线程编程,使得开发者可以在同一进程中同时执行多个线程,实现并发处理任务。本文将深入探讨Windows多线程编程的基本概念、线程同步、线程池以及...
2. **使用自定义消息**:线程可以通过发送自定义的消息来通信。这涉及到Windows的消息机制,发送线程通过调用API函数(如`PostThreadMessage`)向目标线程发送消息,接收线程需在其消息循环中处理这些消息。例如,...
以下是对Java线程安全的深入总结: ### 一、线程安全的定义 线程安全是指当多个线程访问同一块代码时,如果每个线程都能得到预期的结果,且不产生数据不一致或同步问题,那么这块代码就被称为线程安全的。Java中的...
Java多线程是Java编程中的一个核心概念,它在现代软件开发中扮演着至关重要的角色。多线程允许程序同时执行多个任务,提高了系统资源的利用率,提升了应用程序的响应速度和并发性能。对于大型分布式系统、Web应用...
### Java多线程编程总结 #### 一、Java线程:概念与原理 1. **操作系统中线程和进程的概念** - 当前的操作系统通常为多任务操作系统,多线程是实现多任务的一种手段。 - **进程**:指内存中运行的应用程序,每个...
【多线程精心总结】 在Java编程中,多线程是一种重要的并发处理方式,它可以提高程序的执行效率,尤其在处理大量并发任务时。本文将深入探讨Java线程中的几个关键概念,包括`yield()`、`sleep()`、`wait()`、`join...
Java多线程是Java编程语言中一个非常重要的概念,它允许开发者在一个程序中创建多个执行线程并行运行,以提高程序的执行效率和响应速度。在Java中,线程的生命周期包含五个基本状态,分别是新建状态(New)、就绪...
2. **线程同步与通信** - 线程同步:防止多个线程同时访问共享资源,防止数据不一致,例如`synchronized`关键字。 - 等待/通知机制:`wait()`, `notify()`, `notifyAll()`用于线程间的通信,确保线程按特定顺序...
C#.net同步异步SOCKET通讯和多线程总结 C#.net同步异步SOCKET通讯和多线程总结是指在C#.net环境下实现的同步异步套接字通信和多线程编程的总结。套接字(Socket)是tcp/ip网络协议接口,内部定义了许多的函数和例程...
### 线程总结笔记——基于Linux环境下的线程控制与同步 #### 一、引言 本篇“线程总结笔记”主要针对Linux环境下多线程编程中的关键概念进行了整理与归纳,尤其是针对线程同步的问题进行了深入探讨。通过一个具体...
2. **线程的状态** 线程有五种基本状态:新建、就绪、运行、阻塞和终止。通过调用start()方法将线程从新建状态转换为就绪状态,由JVM调度进入运行状态。线程可能会因I/O操作或锁的获取而进入阻塞状态,当任务完成或...
【JAVA多线程总结】 Java 多线程是Java编程中的关键特性,它允许程序同时执行多个任务,提高系统的效率和响应性。本篇总结涵盖了Java多线程的基础概念、创建与启动、线程调度、同步与协作以及新特性。 **一、Java...
C++多线程总结 本文档对C++多线程编程进行了总结,介绍了三种创建线程的方法:CreateThread函数、AfxBeginThread函数和_beginthread()/_beginthreadex()函数,同时对线程的管理和终止进行了详细的讲解。 ...
### 总结 本文从多线程的基础概念出发,深入探讨了Java中多线程的实现机制及线程安全问题,并介绍了几种常见的设计模式(包括单例模式、工厂模式和适配器模式),这些模式有助于解决多线程环境下的常见问题。通过对...
使用 `thread apply ID1 ID2 command` 命令可以让一个或者多个线程执行 GDB 命令 command。 使用 `thread apply all command` 命令可以让所有被调试线程执行 GDB 命令 command。 scheduler-locking 命令 使用 `...
### Java线程总结教程知识点详解 #### 一、操作系统与多线程概念 - **多任务与分时操作系统**:现代操作系统(如Windows、Linux)能够实现多任务处理,即在用户看来似乎多个应用程序在“同时”运行。实际上,这是...
2. 创建线程的方式有哪些? 创建线程的方式有两种:继承Thread类和实现Runnable接口。实现Runnable接口的方式比继承Thread类的方式更灵活,也能减少程序之间的耦合度。 3. start()方法和 run()方法的区别是什么? ...