聊一聊Java多线程

Java多线程

什么是多线程?

多线程,是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。 在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。
个人理解,就是指在巨友多线程能力的计算机因有硬件支持的前提下,充分利用cpu资源,提高性能。

带来的问题

对比单线程和多线程,可以发现,使用了多线程会带来性能的提升,但是也会带来问题,线程数量的控制,过多的线程,不仅不会提升性能,反而可能会引起cpu超负荷运转,导致系统宕机,单线程资源属于线程独享,不用考虑并发安全问题,多线程是多个线程共享资源,对资源的读写会导致线程安全问题

为什么要用多线程

任何技术的革新都是在硬件允许的情况下,硬件发展到今天,cpu 内存都变得极其强大,充分利用硬件资源达到想要的目的是一个高级编程开发人员必备的素养,稳定的程序大多数人都会写,高性能并且稳定的程序才是体现一个高级开发人员的功力的标准。

java对多线程的支持

java 从1.5开始就引进了多线程,对多线程的支持和优化一直未间断,当前多数开发人员使用jdk1.8,对多线程的支持和优化已经达到一个很高的标准

java如何使用多线程(代码)

public static void main(String[] args) {
		Runnable r = new Runnable() {
			@Override
			public void run() {
				System.out.println("------我是线程执行内容");
			}
		};
		Thread t = new Thread(r);
		t.start();
	}

创建一个线程Thread t = new Thread®; 入参Runnable
查看Runnable 代码

@FunctionalInterface
public interface Runnable {
	public abstract void run();
}
//发现Runnable 支持函数式编程,有且只有一个抽象方法,于是改动上面代码
Runnable r = () -> {
			System.out.println("------我是线程执行内容");
		};
Thread t = new Thread(r);
t.start();

查看Thread代码

public class Thread implements Runnable
//params
// 线程name
private volatile String name;
// 线程优先级
private int priority;
//栈长度
private long stackSize;
//线程状态,默认是线程未启动
private int threadStatus = 0;
// 线程私有属性集合
ThreadLocal.ThreadLocalMap threadLocals = null;
....

//使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
start()
//如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
run()
//设置线程优先级
setPriority()
//暂停当前正在执行的线程对象,并执行其他线程。
yield()
//让正在执行的线程休眠
sleep(...)
//等待该线程终止
join(...)
//返回该线程的状态。
getState()
//some else i do not care
...

我们知道,多线程并不关心线程执行顺序,我们只关心最终结果,那么如果想用控制线程执行顺序,需要改变线程状态
threadStatus 或者设置优先级,但是如果要精准的控制多个线程的执行顺序,只能依靠线程状态

线程状态
上面说过,Thread类有线程状态属性和对应的get set 方法那么操作的是什么呢?

//线程状态
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread.  A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
*   <li>{@link Object#wait() Object.wait} with no timeout</li>
*   <li>{@link #join() Thread.join} with no timeout</li>
*   <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
TIMED_WAITING,
TERMINATED;
}

那么稍微修改,上面代码查看一下线程状态

public static void main(String[] args) throws Exception {
		Runnable r = () -> {
			System.out.println("------我是线程执行内容");
		};
		Thread t = new Thread(r);
		System.out.println(t.getState());
		t.start();
		System.out.println(t.getState());
		Thread.sleep(1000);
		System.out.println(t.getState());
	}
	//console.out
	NEW
	RUNNABLE
	------我是线程执行内容
	TERMINATED
	//可以很清晰的看到线程的状态在线程的生命周期内
	new Thread() --- > NEW
	t.start() ----> RUNNABLE
	//获取到cpu执行片段,开始执行时间较短  ------我是线程执行内容 --- > Running
	Thread.sleep(1000); ---> 等待线程执行结束 ----> TERMINATED

ok,那么我们来查看一下,能够修改线程执行状态的方法,以及修改的对应线程状态内容,同时看一下是否有其他比较有意思的东西

//来自object的
wait()
notify()
notifyAll()
//来自Thread
start()
sleep()
join()
yield()
// or some else i did not found ..haha

那我们,随便看一个方法

eg:
/**
*Causes the current thread to wait until either another thread invokes the 
* or a specified amount of time has elapsed.
* -- 使当前线程等待,直到另一个线程调用notify or notifyAll 或者timeout时间已经过去了
* The current thread must own this object's monitor.
* 当前线程必须拥有此对象的监视器。
*/
public final native void wait(long timeout) throws InterruptedException;

哇!发现了一个新东西object's monitor,这是什么呢?

monitor(object’s monitor)

哇!本来是看多线程的,怎么出了个object’s monitor,这是什么鬼,还是object’s 那么每个object都有啊。
带着这个疑问,查看jdk代码,咦,居然没有发现,遂查阅网上大神分享资料发现好玩的东西

java对象的堆内存结构:
我们知道创建一个Object,那么Object在内存中都存了些什么东西呢?


java对象在堆中的基本内存结构,分为三个部分:
  • 对象头(header):包括Mark Word(标记字段)和Class Pointer(类型指针)
  • 实例数据(instance data):对象真正存储的有效信息,即代码中定义的各种类型的字段内容
  • 对齐填充(padding):由HotSpot虚拟机定义对象起始地址必须是8字节整数倍,当不是整数倍时,需要填充数据补齐,因为对补齐的数据访问只需要一次内存访问即可

这就很有意思了,来看一下
对象头(header)
用于存储对象自身运行时数据,如HashCode、GC分代年龄、锁状态标志、线程持有锁、偏向线程ID、偏向时间戳等
占用内存为一个机器码,32位系统一个机器码为4个字节,64位系统一个机器码为8字节
以32位系统为例:25Bit-对象的hashCode、4Bit-对象的分代年龄、1Bit-是否偏向锁、2Bit-锁标志位
通过MarkOop类型实现Mark Word,提供了大量方法用于查看当前对象头的状态,以及更新对象头的数据,为synchronized锁的实现提供了基础
不同锁状态下,存储数据不同,具体如下:
在这里插入图片描述
这就很有意思了,发现了新天地,原来每个对象在创建的时候都分配了锁,和一些JVM 操作相关的参数,指针分代年龄啊布拉布拉的。。。头好大,大神写代码这么节省内存的吗,用位存储。。。我这动不动就List map的真差劲哦~废话好多,继续看Monitor


Monitor的基本结构是什么?
  • Owner字段:初始时为NULL表示当前没有任何线程拥有该monitor record,当线程成功拥有该锁后保存线程唯一标识,当锁被释放时又设置为NULL
  • EntryQ字段:关联一个系统互斥锁(semaphore),阻塞所有试图锁住monitor record失败的线程
  • RcThis字段:表示blocked或waiting在该monitor record上的所有线程的个数
  • Nest字段:用来实现重入锁的计数
  • HashCode字段:保存从对象头拷贝过来的HashCode值(可能还包含GC age)
  • Candidate字段:用来避免不必要的阻塞或等待线程唤醒,因为每一次只有一个线程能够成功拥有锁,如果每次前一个释放锁的线程唤醒所有正在阻塞或等待的线程,会引起不必要的上下文切换(从阻塞到就绪然后因为竞争锁失败又被阻塞)从而导致性能严重下降;Candidate只有两种可能的值0表示没有需要唤醒的线程1表示要唤醒一个继任线程来竞争锁

Monitor与java对象以及线程是如何关联的?
- 如果一个java对象被某个线程锁住,则该java对象的Mark Word字段中LockWord指向monitor的起始地址
- Monitor的Owner字段会存放拥有相关联对象锁的线程id
Monitor具体是怎么实现的?
1.Monitor是在jvm底层实现的,底层代码是c++
2.Monitor的enter方法:获取锁
3.Monitor的exit方法:释放锁
4.Monitor的wait方法:为java的Object的wait方法提供支持
5.Monitor的notify方法:为java的Object的notify方法提供支持
6.Monitor的notifyAll方法:为java的Object的notifyAll方法提供支持

不行了,篇幅太长了,视觉疲劳,思维混乱,在写一篇~
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,让我来回答你的问题。要设计一个简单的TCP Socket对话程序可以使用Java的Socket类。使用多线程可以实现多个客户端之间的同时对话。 首先,需要创建一个服务器端程序,监听来自客户端的连接请求。可以使用ServerSocket类创建一个服务器套接字。当有客户端请求连接时,服务器端会接受连接并创建新的线程处理该客户端的请求。在处理过程中,可以使用Socket类来与客户端进行通信。 以下是简单的示例代码: ```java import java.net.*; import java.io.*; public class TCPServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(8888); System.out.println("Server started."); while (true) { Socket clientSocket = serverSocket.accept(); System.out.println("Client connected: " + clientSocket.getInetAddress().getHostAddress()); // 创建新的线程处理客户端请求 Thread clientThread = new ClientHandler(clientSocket); clientThread.start(); } } } class ClientHandler extends Thread { private Socket clientSocket; private PrintWriter out; private BufferedReader in; public ClientHandler(Socket socket) throws IOException { this.clientSocket = socket; out = new PrintWriter(clientSocket.getOutputStream(), true); in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); } public void run() { String inputLine; try { while ((inputLine = in.readLine()) != null) { // 处理客户端发来的消息 System.out.println("Received message from client: " + inputLine); out.println("Server: " + inputLine); } } catch (IOException e) { e.printStackTrace(); } try { clientSocket.close(); System.out.println("Client disconnected."); } catch (IOException e) { e.printStackTrace(); } } } ``` 以上代码中,ServerSocket监听端口8888,并不断接收客户端的连接请求。每当有客户端连接成功后,就会创建一个新的线程处理该客户端的请求。ClientHandler线程中,使用PrintWriter输出流向客户端发送消息,并使用BufferedReader输入流接收客户端发送的消息。 如果要测试程序,可以使用telnet或其他工具模拟客户端连接,然后通过输入输出流进行对话。当然,也可以编写客户端程序与该TCP Socket对话程序进行交互。 希望这个简单的示例对你有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值