多线程
Posted python-road
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程相关的知识,希望对你有一定的参考价值。
多线程
进程是执行程序的一次执行过程,是系统资源分配的单位;一个进程可以包含若干个线程,线程是CPU调度和执行的单位;
一.继承Thread类
- 自定义线程类继承Thread类
- 重写run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
package oop;
//线程开启不一定执行,由CPU调度执行
public class Test2 extends Thread{
@Override
public void run() {
for(int i=0;i<10;i++)
System.out.println("看什么呢");
}
public static void main(String[] args) {
Test2 test2=new Test2();
test2.start();
for (int i=0;i<1000;i++){
System.out.println("我在吃"+i);
}
}
}
网图下载
package oop;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//实现多线程同步下载图片
public class Test2 extends Thread{
private String url;
private String name;
public Test2(String url,String name){
this.name=name;
this.url=url;
}
@Override
public void run() {
WebDownloader webDownloader=new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("下载的文件名围殴"+name);
}
class WebDownloader{
public void downloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
}catch(IOException e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Test2 test1=new Test2("https://t8.baidu.com/1.jpg","1.jpg");
Test2 test2=new Test2("https://t8.baidu.com/2.jpg","2.jpg");
Test2 test3=new Test2("https://t8.baidu.com/3.jpg","3.jpg");
test1.start();
test2.start();
test3.start();
}
}
二.实现Runable接口
创建线程方法2:实现runable接口,重写run方法,执行线程需要丢入runable接口实现类,调用start方法
package oop;
public class Test2 implements Runnable{
@Override
public void run() {
for(int i=0;i<10;i++)
System.out.println("看什么呢");
}
public static void main(String[] args) {
//创建runnable接口的实现类对象
Test2 test2=new Test2();
Thread thread=new Thread(test2);
thread.start();
for(int i=0;i<10;i++){
System.out.println("我在学习!");
}
}
}
三.继承Thread类和实现Runnable接口对比
- 继承Thread类
- 子类继承Thread类具备多线程能力
- 启动线程:子类对象.start()
- 不建议使用:单继承局限性
- 实现Runnable接口
- 实现接口Runnable具有多线程能力
- 启动线程:传入目标对象+Thread对象.start()
- 可以避免单继承局限性,灵活,方便同一对象被多个线程使用
多线程同时使用实现Runnable接口对象
package oop;
public class Test2 implements Runnable {
private int ticketNums = 10;
@Override
public void run() {
while (true) {
if (ticketNums <= 0) {
break;
}
System.out.println(Thread.currentThread().getName() + "拿到了底" + ticketNums + "票");
ticketNums--;
}
}
public static void main(String[] args) {
//创建runnable接口的实现类对象
Test2 test2 = new Test2();
new Thread(test2, "ab").start();
new Thread(test2, "cd").start();
new Thread(test2, "ef").start();
new Thread(test2, "hi").start();
}
}
四.静态代理
真实对象和代理对象都要实现同一个接口
代理对象要代理真实角色
package oop;
public class Test2{
public static void main(String[] args) {
WeddingCompany weddingCompany=new WeddingCompany(new You());
weddingCompany.HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
class You implements Marry{
@Override
public void HappyMarry() {
System.out.println("结婚了");
}
}
class WeddingCompany implements Marry{
private You target;
public WeddingCompany(You target){
this.target=target;
}
@Override
public void HappyMarry() {
before();
this.target.HappyMarry();
after();
}
private void before(){
System.out.println("结婚前");
}
private void after(){
System.out.println("结婚后");
}
}
代理的好处:代理对象可以做很多真实对象做不了的事情
真实对象能专注做直接的事情
五.lambda表达式
函数式接口:任何接口如果只包含一个抽象方法,那么他就是一个函数式接口;对于函数式接口,可以通过lambda表达式来创建该接口的对象
package oop;
public class Test2{
public static void main(String[] args) {
//形式1.
Like like1= (int a)->{ System.out.println("我爱你"); };
//形式2.
Like like2=(a)->{
System.out.println("你忙吧");
};
Like like3=a-> System.out.println("hhh");
like1.lambda(1);
like2.lambda(1);
like3.lambda(1);
}
}
interface Like{
void lambda(int a);
}
六.线程停止
通过外部标志位和外部停止方法进行停止
package oop;
public class Test2 implements Runnable{
private boolean flag=true;
@Override
public void run() {
int i=0;
while(flag){
System.out.println("Run"+i++);
}
}
public void stop(){
this.flag=false;
}
public static void main(String[] args) {
Test2 test2=new Test2();
new Thread(test2).start();
for(int i=0;i<100;i++){
System.out.println("main"+i);
if(i==90){
System.out.println("线程该停止了");
test2.stop();
}
}
}
}
七.线程休眠
- sleep(时间)指定当前线程阻塞的毫秒数;1000毫秒=1秒
- sleep存在异常InterruptedException
- sleep时间达到后线程进入就绪状态
- 可以模拟倒计时和网络延时
- 每个对象都有锁,sleep不会释放锁
八.线程礼让
- 礼让线程,让当前正在执行的线程暂停,单不阻塞
- 将线程从运行状态转为就绪状态
- 让cpu重新调度,礼让不一定成功,看CPU调度
package oop;
public class Test2{
public static void main(String[] args) {
MyYield myYield=new MyYield();
new Thread(myYield,"dwx").start();
new Thread(myYield,"zzz").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
线程状态
- NEW:尚未启动的线程处于此状态
- RUNNABLE:在Java虚拟机中执行的线程处于此状态
- BLOCKED:被阻塞等待监视器锁定的线程处于此状态
- WAITING:正在等待另一个线程执行特定动作的线程处于此状态
- TIMED_WAITING:正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
- TERMINATED:已退出的线程处于此状态
package oop;
public class Test2{
public static void main(String[] args) {
Thread thread=new Thread(()->{
for(int i=0;i<5;i++){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println("------");
});
Thread.State state=thread.getState();
System.out.println(state);
thread.start();
state=thread.getState();
System.out.println(state);
while(state!=Thread.State.TERMINATED){
state=thread.getState();
System.out.println(state);
}
state=thread.getState();
System.out.println(state);
}
}
十.线程的优先级
- Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定调度哪个线程来执行
- 线程的优先级用数字表示,范围从1-10
- Thread.MIN_PRIORITY=1;
- Thread.MAX_PRIORITY=10;
- Thread.NORM_PRIORITY=5;
改变或获取优先级
getPriority(); setPriority(int XXX)
设置在start()之前
十一.守护线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 守护线程有后台记录操作日志,监控内存,垃圾回收等
package oop;
public class Test2{
public static void main(String[] args) {
God god =new God();
You you =new You();
Thread thread=new Thread(god);
thread.setDaemon(true);//默认为false,普通线程,设置为true后为守护线程。
thread.start();
new Thread(you).start();
}
}
class God implements Runnable{
@Override
public void run() {
while(true){
System.out.println("上帝保佑你");
}
}
}
class You implements Runnable{
@Override
public void run() {
for(int i=1;i<365;i++){
System.out.println("这是我生命中的第"+i+"天");
}
}
}
十二.线程同步
并发:同一个对象被多个线程同时操作
线程同步:其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。
同步方法和同步块
给方法加上synchronized关键字即可,synchronized方法控制对对象的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,同步块就是synchronized(obj){},要同步就是要锁变化的量
package oop;
import java.util.ArrayList;
import java.util.List;
//不安全的买票
public class UnsafeList{
public static void main(String[] args) {
List<String> list=new ArrayList<>();
for(int i=0;i<1000;i++){
new Thread(()->{
synchronized(list){
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(list.size());
}
}
死锁
产生死锁的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺条件:进程已获得的资源在为使用完之前,不能强行剥夺
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
只要破解其中的任意一个或多个条件就能避免死锁
Lock(锁)
java.util.concurrent.locks.Lock接口,ReentrantLock类实现了Lock,可以显式加锁释放锁
ReentrantLock lock=new ReentrantLock();
lock.lock();//加锁
lock.unlock();//解锁
十三.线程通信
java提供了几个方法解决线程之间的通信问题
- wait():表示线程一直等待,知道其他线程通知,与sleep不同,会释放锁
- wait(long timeout):指定等待的毫秒数
- notify():唤醒一个处于等待状态的线程
- notifyAll():唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度
解决生产者消费者问题
两种解决方法:
- 管程法:生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
- 信号灯法:增加标志位,用标志位来通知生产者和消费者谁唤醒谁等待
十四.线程池
提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁创建销毁,实现重复利用
优点:
- 提高响应速度
- 降低资源消耗
- 便于线程管理
package oop;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestPool{
public static void main(String[] args) {
//创建线程池
ExecutorService service= Executors.newFixedThreadPool(10);
//执行
service.execute(new MyThread());
//关闭连接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}
以上是关于多线程的主要内容,如果未能解决你的问题,请参考以下文章