Java 之多线程
程序员文章站
2023-12-23 13:33:34
...
Java 多线程
进程:
- 概述:也就是只正在运行的程序,cup资源分配的独立单位;只是为了获得资源;
- 优点:提高cpu的使用率;这一点很多人不容理解:假如你现在是一个单核计算机,你开了好几个视频窗口在看视频(打开腾讯看NBA,同时打开了优酷在看电影而且同时有使用爱奇艺看电视剧,哈哈,夸张了哈),你同时将他们窗口缩小放在放在桌面上,想看哪个就看哪个,此时你可能会感觉他们是同一时间在共同运行着,这其实就是多进程的体现;那为什说多进程可以提高cpu的使用率呢?你既然可以同时打开多个软件去运行,而且这种感受让你感觉他们是共同运行着,你却只打开一个软件,是不是浪费了,是不是可以理解了呢!
线程:
- 概述:进程中的单个顺序控制流,是一条执行路径,多线程也就是说一个进程中有多个执行路径;
- 多线程的优点:提高cpu利用率;对于这一点的理解,也可以参考多进程的思维方式,比如说你用爱奇艺看视频的时候,爱奇艺软件上可能还有时间日期显示等,这是即浏览这视频线程,又可以去看时间几点了。
tips:
-
线程与线程共享“堆内存和方法区内存”,栈内存是独立的,一个线程一个栈;
-
有个多线程之后,main方法结束只是主线程栈中没有方法栈帧了,但是其他线程或者其他栈中还有栈帧,也就是说main方法结束后,程序可能还在运行。
多线程的生命周期:*
- 一般会经历五个阶段:新建,就绪,运行,阻塞,死亡;
线程调度:
- 分时调度:
- 抢占式调度:java采用抢占式调度
线程控制语句**:
-
public static void sleep(long millis) :线程休眠,注意它是静态的方法,该方法出现在哪个线程中,哪个线程将进入阻塞状态,也就是线程停止执行了,待休眠的时间过后又将回到就绪阶段,争夺cup时间片。阻塞但不释放锁。
-
public void interrupt();线程中断(指的是当线程中有sleep,wait导致线程执行过程中受阻,interrupt可以去清除这种状态,然后继续往下执行。
-
public final void join(): 加入线程,其他线程需要等它执行完毕后才能执行。加入线程的语句位置为:
//如果要让t1必须t2执行完后在执行,那么它的代码顺序:
//t2必须放在开始,而且jion必须跟在后面
t2.start();
t2.join();
t1.start();
-
public static void** yield()**;礼让线程,让多个线程执行更加和谐,但不能保证每个线程一人一次。
-
public final void** setDaemon**(boolean on)守候线程,当只剩下守候线程的时候,虚拟机停止,该方法必须在启动线程前调用。(垃圾回收器就是一个守候线程)
同步代码块、同步方法及静态方法的锁***
- 在上篇文章中说过线程的不安全问题,本章节在这块说明下如何去解决多线程的不安全问题:
线程不安全问题的本质是由于线程执行的随机性,导致我们无法预期它的执行结果,所以当我们可以控制它们的执行过程,是不是就可以解决线程的不安全问题了!具体怎么操作呢?
当一个代码块或者方法被synchronized修饰的时候,线程在执行的这里的时候,需要等待里面已有线程执行完毕后才能去执行,被synchronized修饰后会伴随有一个锁, 同步代码块、同步方法的锁称为对象锁,对象锁有多个,静态方法的锁称为类锁,类锁只有一个。
同步代码块及其锁:
- 被synchronized括起来的代码就成为同步代码块,synchronized就类似给代码块安了个门,里面的this可以看做是一把锁,这个锁是一个具体的对象,可以是任意类的对象,也就是说你可以自己创建个类后创建个对象使用于此。
//注意,这里使用的多线程方式是实现Runnable接口
public class Test implements Runnable {
int i=100;
public void run() {
while (i>0){
synchronized (this){
//当有多个线程执行(t1,t2,t3),当t1进入到 synchronized 中后,门就被锁上了,其它两
//个线程执行到这里的时候需要在次等待,等t1执行完成后释放了锁,然后才能再去争抢执行权
if(i>0){
System.out.println(Thread.currentThread.getName()+"------"+i--);
}
}
}
}
}
同步方法及其锁:
- 同步方法:当一个类中的成员方法被**synchronized**修饰的时候,它就是同步方法;
同步方法的锁是当前所创建的对象。如下代码class MyClass中的两个方法m1()和m2()都是同步方法,当这两个方法被run()调用的时候,它的锁对象就是MyClass m=new MyClass();当被多个线程执行的时候,这两个方法又是怎么执行的呢?
详细的过程描述见代码中:
public class Test{
public static void main(String[] args) throws InterruptedException {
S s = new S();
Thread s1=new Thread(s);
Thread s2=new Thread(s);
s1.setName("m1");
s2.setName("m2");
s2.start();
s1.start();
// Thread.sleep(1000);
}
}
class S implements Runnable{
MyClass m=new MyClass();
//MyClass s=new MyClass();
public void run() {
if(Thread.currentThread().getName().equals("m1")){
m.m1();
//MyClass中的成员方法m1()和m2()都被synchronized所修饰,所以在运行这段程序的
//时候,如果m1线程先抢到执行权的话,那么m2线程必须m1线程执行完成后才能执行,
//他们的锁对象就是当前创建的类的对象(MyClass m=new MyClass())
//线程与线程之间需不需要等待,只需要判断他们是不是同一把锁。
}else {
s.m2();
}
}
}
class MyClass{
public synchronized void m1(){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m1........");
}
public synchronized void m2(){
//如果将m2****()方法上的synchronized去除,那么m2线程将不用在等待m1了
System.out.println("m2.............");
}
}
静态同步方法及其锁
- 静态同步方法:顾名思义,同时被静态及同步所修饰的方法;
- 静态同步方法的锁可以称之为类锁,类锁只有一个,这个怎么理解呢?看下面代码:
public class Test01 {
public static void main(String[] args) {
S s1=new S();
S s2=new S();
Thread m1=new Thread(s1);
Thread m2=new Thread(s2);
m1.setName("m1");
m2.setName("m2");
m1.start();
m2.start();
}
}
class S implements Runnable{
@Override
public void run() {
if(Thread.currentThread().getName().equals("m1")){
MyClass.m1();
}else {
MyClass.m2();
//由于m1和m2是静态同步方法,所以他们使用的锁是类锁,又因为类锁这有一个,所以无
//论m1或m2谁先抢到cpu时间片,另一个都必须等其执行完成后在执行
}
}
}
class MyClass{
public synchronized static void m1(){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m1..........");
}
public synchronized static void m2(){
System.out.println("m2.........");
}
}