多线程

Posted DFshmily

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程相关的知识,希望对你有一定的参考价值。

内容

  • 什么是线程
  • 如何创建线程
  • 线程的调度
  • 线程的一个设计模式:生产消费者模型
  • 线程池
  • 线程集合对象(侧重点)

一、什么是线程

进程:运行中的程序才可以称为进程,一个程序一个进程。宏观并行,微观串行。

线程:

1.任何一个程序都至少拥有一个线程,即主线程。但是java程序默认有两个线程,除了主线程之外,还有一个线程,即用于垃圾回收的守护线程。

2.线程是一种轻量级进程,在CPU当中的最基本单元是线程。

3.一个进程可以包含若干的线程

4.各进程之间不共享内存,同进程的各线程之间共享内存

5.每个线程都有独立的栈空间

6.堆当中的地址,可以被共享

注意:

线程是不可控的,只能调度,无法精确控制。研究线程,研究的是如何在多线程场景中,保证数据读写的安全,以及调度线程的运行状态

二、线程的创建和使用

1.通过Thread类来进行创建,需要继承Thread类

package com.mine.demo01;
public class Main1 
    public static void main(String[] args) 
        MyThread1 mt1 = new MyThread1();
        mt1.start();//调用的不是run方法,而是start
    

class MyThread1 extends Thread
    @Override
    public void run() 
        System.out.println("线程运行了");
    

2.通过实现Runnable接口来创建

package com.mine.demo01;
public class Main2 
    public static void main(String[] args) 
        Thread t = new Thread(new MyThread2());
        t.start();
    

class MyThread2 implements Runnable
    @Override
    public void run() 
        System.out.println("线程被运行了");
    

每个线程的逻辑,都是在run方法当中运行的。通过调用start方法来执行线程。start方法只能被调用一次。

3.线程名的创建和获取

Thread t = new Thread(new MyThread2(),"mythreadName2");
t.setName("myNewThreadName2");
t.start();

线程的线程名,需要在启动之前就确定好。

每个线程都有一个默认的名字:Thread-N,N是线程建立的顺序,是一个不重复的正整数

4.获取线程名的方式

在每一个线程的线程栈当中,调用Thread.currentThread().getName()获取线程名

package com.mine.demo01;
public class Main3 
    public static void main(String[] args) 
        System.out.println("主线程:"+Thread.currentThread().getName());
        Thread t = new Thread(new MyThread3());
        t.start();
    

class MyThread3 implements Runnable 
    @Override
    public void run() 
        System.out.println("子线程:" + Thread.currentThread().getName());
    


结果:
主线程:main
子线程:Thread-0

三、线程调度

线程之间传参,共享数据

synchronized:同一时刻,只有一个线程可以调用该方法。

package com.mine.demo01;
public class Main4 
    public static void main(String[] args) 
        Test t = new Test();
        for (int i = 0 ; i < 1000 ; i ++)
            new Thread(new Thread1(t)).start();
        
    

//线程数据的共享和传参
class Test
    private int i = 0;
    //为了保证数据安全,需要用到同步锁
    public synchronized int getI() 
        return ++i;
    

class Thread1 implements Runnable
    private Test t;
    public Thread1(Test t)
        this.t = t;
    
    @Override
    public void run() 
        System.out.println(t.getI());
    

  • 一个正常线程的生命周期

1.新建线程(1生命形态)
2.启动线程
3.就绪状态(2就绪形态)

4.等待系统分配资源
5.运行状态(3运行形态)
6.死亡状态(4死亡形态)

1.等待

等待的构成:
1.新建
2.就绪
3.运行
4.持锁(先到就绪状态---->运行)
5.等待
6.释放锁
7.时间到或者被唤醒
8.持锁(先到就绪状态---->运行)
9.执行等待后续
10.死亡

注意:等待必须在同步环境当中,如果不在同步环境中,会报如下异常

java.lang.RuntimeException: java.lang.IllegalMonitorStateException
	at com.qf.demo01.Test.getI(Main4.java:18)
	at com.qf.demo01.Thread1.run(Main4.java:30)
	at java.lang.Thread.run(Thread.java:745)
package com.mine.demo01;

public class Main4 
    public static void main(String[] args) 
        Test t = new Test();
        for (int i = 0; i < 3; i++) 
            new Thread(new Thread1(t)).start();
        
    


class Test 
    private int i = 0;

    public synchronized int getI() 
        try 
            System.out.println("等待开始前:" + Thread.currentThread().getName());
            this.wait(3000);
            System.out.println("等待开始后:" + Thread.currentThread().getName());
         catch (Exception e) 
            throw new RuntimeException(e);
        
        return ++i;
    


class Thread1 implements Runnable 
    private Test t;

    public Thread1(Test t) 
        this.t = t;
    

    @Override
    public void run() 
        System.out.println(t.getI());
    

运行结果:

等待开始前:Thread-0
等待开始前:Thread-2
等待开始前:Thread-1
等待开始后:Thread-2
1
等待开始后:Thread-1
2
等待开始后:Thread-0
3

注意:只要是线程进入阻塞,那么他一定回先回到就绪状态

2.休眠

package com.mine.demo05;

public class Main 
    private synchronized static void test()
        try 
            System.out.println("休眠前:"+Thread.currentThread().getName());
            Thread.sleep(3000);
            System.out.println("休眠后:"+Thread.currentThread().getName());
        catch (InterruptedException e)
            throw new RuntimeException(e);
        
    
    public static void main(String[] args) 
        for (int i = 0;i < 2 ;i++)
            Thread t = new Thread(new Runnable() 
                @Override
                public void run() 
                    test();
                
            );
            t.start();
        
    


结果:
休眠前:Thread-0
休眠后:Thread-0
休眠前:Thread-1
休眠后:Thread-1

注意:休眠不释放锁,休眠不可被唤醒,必须等到休眠时间结束

3.优先级(了解)

Thread t = new MyThread();
t.setPriority(5);
t.start();

4.让步(了解)

Thread.yield();//作用仅仅是让当前正在运行的线程回到就绪状态

5.线程的join(生命周期和sleep是一样的)

package com.mine.demo06;

public class Main 
    public static void main(String[] args) 
        Thread t1 = new Thread(new Runnable() 
            @Override
            public void run() 
                System.out.println(Thread.currentThread().getName()+"......");
                try 
                    Thread.sleep(3000);//3s
                 catch (InterruptedException e) 
                    throw new RuntimeException(e);
                
            
        );
        Thread t2 = new Thread(new Runnable() 
            @Override
            public void run() 
                System.out.println(Thread.currentThread().getName()+"++++++");
                try 
                    Thread.sleep(3000);
                 catch (InterruptedException e) 
                    throw new RuntimeException(e);
                
            
        );

        try 
            t1.start();
            t1.join();
            t2.start();
            t2.join();
         catch (InterruptedException e) 
            throw new RuntimeException(e);
        

    



结果:
Thread-0......
Thread-1++++++

6.守护线程

用户线程:主帅,用户线程只要在运行,守护线程可以一直运行下去,除非守护线程自行死亡

守护线程:卫兵,只要没有用户线程还在继续执行,那么无论其是否还存活,都将立即死亡

四、同步锁

注意:只要是改数据就,就一定要考虑数据同步问题

  • 对象锁
  //同步方法
    public synchronized void getI() 
        System.out.println(Thread.currentThread().getName()+"持锁");
        try 
            Thread.sleep(5000);
         catch (InterruptedException e) 
            throw new RuntimeException(e);
        
    
public void test()
        System.out.println(Thread.currentThread().getName()+" "+i);
        synchronized (this)//同步代码块
            System.out.println(Thread.currentThread().getName()+"持锁");
            try 
                Thread.sleep(3000);
                i++;
             catch (InterruptedException e) 
                throw new RuntimeException(e);
            
        
        System.out.println(i);
    

基于对象的锁,必须是多线程共享的同一个对象

 //基于对象的锁
        private A a = new A();
        public void test()
            // A a = new A();
            System.out.println(Thread.currentThread().getName()+" "+i);
            synchronized(a)
                System.out.println(Thread.currentThread().getName()+"持锁");
                try 
                    Thread.sleep(3000);
                    i++;
                 catch (InterruptedException e) 
                    throw new RuntimeException(e);
                
            
            System.out.println(i);
        
  • 类锁
package com.mine.demo02;
public class Main2 
    public static void main(String[] args) 
        for(int i = 0 ; i < 3 ; i ++)
            new Thread(new Runnable() 
                @Override
                public void run() 
                    C.fun1();
                
            ).start();
        
    

class C
    public synchronized static void fun1()
        try 
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(3000);
         catch (InterruptedException e) 
            throw new RuntimeException(e);
        
    

public static void fun2()
	synchronized(C.class)
		try 
			System.out.println(Thread.currentThread().getName());
			Thread.sleep(3000);
		 catch (InterruptedException e) 
				throw new RuntimeException(e);
		
    

  • 死锁
package com.mine.demo02;
public class Main1 
    public static void main(String[] args) 
        Test t = new Test();
        new Thread(new Thread1(t)).start();
        new Thread(new Thread2(t)).start();
    

class Test
    private A a = new A();
    private B b = new B();
    public void test1()
        synchronized(b)
            System.out.println(Thread.currentThread().getName()+"持b锁");
            try 
                Thread.sleep(1000);
             catch (InterruptedException e) 
                throw new RuntimeException(e);
            
            synchronized(a)
                System.out.println(Thread.currentThread().getName()+"持a锁");
            
        
    
    public void test2()
        synchronized(a)
            System.out.println(Thread.currentThread().getName()+"持a锁");
            try 
                Thread.sleep(1000);
             catch (InterruptedException e) 
                throw new RuntimeException(e);
            
            synchronized(b)
                System.out.println(Thread.currentThread().getName()+"持b锁");
            
        
    

class Thread1 implements Runnable
    private Test t;
    public Thread1(Test t)
        this.t = t;
    
    @Override
    public void run() 
        t.test1();
    

class Thread2 implements Runnable
    private Test t;
    public Thread2(Test t)
        this.t = t;
    
    @Override
    public void run() 
        t.test2();
    

class A
class B

注意:
1.静态锁只和静态锁一起用,对象锁只和对象锁一起用,不要混用。
2.持锁顺序保持一致

五、volatile关键字

是一个轻量级的同步锁,因为互斥是同步中最消耗性能的地方,而在读-读/读-写(多读1写),那么这
种情况就只需要考虑可见性。
同步锁的功能包括:互斥,可见

package com.mine.demo02;
public class Person 
    private volatile Integer id;
    private volatile String name;
    public Integer getId() 
        return id;
    
    public synchronized void setId(Integer id) 
        this.id = id;
    
    public String getName() 
        return name;
    
    public synchronized void setName(String name) 
        this.name = name;
    

六、生产消费者模型

package com.mine.demo03;
import java.util.Date;
//生成者
public class Producer implements Runnable 
    private final Queue queue;
    public Producer(final Queue queue) 
        this.queue = queue;
    
    @Override
    public void run() 
        for(;;)
            try 
                Thread.sleep(1000);
                this.queue.add(Thread.currentThread().getName() + " " + new
                        Date().getTime());
             catch (InterruptedException e) 
                throw new RuntimeException(e);
            
        
    


package com.mine.demo03;
import java.util.Date;
//消费者
public class Consumer implements Runnable
    private final Queue queue;
    public Consumer(final Queue queue) 
        this.queue = queue;
    
    @Override
    public void run() 
        for(;;)
            try 
                Thread.sleep(1000);
                System.out.println(this.queue.get());
             catch (InterruptedException e) 
                throw new RuntimeException(e);
            
        
    



package com.qf.demo03;
import java.util.LinkedList;
//队列
public class Queue
    private static final int MAX_VALUE = 5;
    private LinkedList queue = new LinkedList();
    public synchronized void add(Object obj)
        while(queue.size()==MAX_VALUE)//用while不要用if
            try 
                //满了等待
                System.out.println(Thread.currentThread().getName() + " 满了,等
                        待!!");
                this.wait();
             catch (InterruptedException e) 
                throw new RuntimeException(e);
            
        
        //没有满,添加
        this.queue.addFirst(obj);
        //每次添加唤醒全部线程
        this.notifyAll();
    
    public synchronized Object get()
        while (this.queue.size() == 0)
            //空了等待
            try 
                System.out.println(Thread.currentThread().getName() + " 空了,等
                        待!!");
                this.wait();
             catch (InterruptedException e) 
                throw new RuntimeException(e);
            
        
        //不为空
        Object obj = this.queue.getLast();
        this.queue.removeLast();
        //每次获取唤醒全部线程
        this.notifyAll();
        return obj;
    

package com.mine.demo03;

import com.oracle.jrockit.jfr.Producer;

import java.util.function.Consumer;

public class Main 
    public static void main(String[] args) 
        Queue queue = new Queue();
        new Thread(new Producer(queue)).start();
        new Thread(new Producer(queue)).start();
        new Thread(new Consumer(queue)).start();
    

python多线程和多线程问题

单线程爬虫和多线程爬虫哪个封IP的几率高。延时一样

那肯定是多线程,爬的频率更高了 参考技术A 当然是多线程了。如果使用代理IP的话,封ip的概率就会降低。

以上是关于多线程的主要内容,如果未能解决你的问题,请参考以下文章

什么是多线程,多进程?

多线程和多进程模式有啥区别

多线程Java多线程学习笔记 | 多线程基础知识

java中啥叫做线程?啥叫多线程?多线程的特点是啥

c++ 多线程与c多线程有啥区别?

IOS多线程安全(线程锁)