多线程
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的概率就会降低。以上是关于多线程的主要内容,如果未能解决你的问题,请参考以下文章