多线程学习

Process 和 Tread

创建多线程方法一

复制代码
 1 package main.com;
 2 
 3 
 4 //创建进程方法一:继承 Thread 类,重写 run() 方法,调用 start() 方法开启线程
 5 
 6 //总结:注意,线程开启不一定立即执行由 CPU 调度执行
 7 public class TestThread1 extends Thread{
 8 
 9     @Override
10     public void run(){
11         //run 方法线程体
12         for (int i = 0; i < 20; i++) {
13             System.out.println("runing..." + i);
14         }
15 
16     }
17 
18     public static void main(String[] args) {
19         //main 线程,即主线程
20 
21         TestThread1 tt1 = new TestThread1();
22         //调用start() 方法开启线程
23         tt1.start();
24 
25         for (int i = 0; i < 200; i++) {
26             System.out.println("main....." + i);
27         }
28 
29     }
30 }
复制代码

 实践

复制代码
 1 package com.xun;
 2 
 3 import org.apache.commons.io.FileUtils;
 4 
 5 import java.io.File;
 6 import java.io.IOException;
 7 import java.net.URL;
 8 
 9 //练习Thread,实现多线程同步下载图片
10 public class TestThread2 extends Thread{
11     private String url; //网络图片地址
12     private String name;    //保存的文件名
13 
14     public TestThread2(String url,String name){
15         this.url = url;
16         this.name = name;
17     }
18 
19     @Override
20     public void run(){
21         WebDownloder webDownloder = new WebDownloder();
22         webDownloder.downloader(url,name);
23         System.out.println("下载文件名为"+name);
24     }
25 
26     public static void main(String[] args) {
27         TestThread2 t1 = new TestThread2("http://www.ikuimg.com/img/pic/2203/1-220331105120-p9.jpg","1.jpg");
28         TestThread2 t2 = new TestThread2("http://www.ikuimg.com/img/pic/2203/1-220331105121-p9.jpg","2.jpg");
29         TestThread2 t3 = new TestThread2("http://www.ikuimg.com/img/pic/2203/1-220331105122-p9.jpg","3.jpg");
30 
31         t1.start();
32         t2.start();
33         t3.start();
34 
35     }
36 }
37 
38 //下载器
39 class WebDownloder{
40     public void downloader(String link, String name){
41         try {
42             URL url = new URL(link);
43             //url.openConnection().setRequestProperty("User-Agent", "Mozilla/4.76");
44             FileUtils.copyURLToFile(url,new File(name));
45         } catch (IOException e) {
46             e.printStackTrace();
47             System.out.println("IO异常,downloder方法异常");
48         }
49     }
50 }
同步下载图片
复制代码

创建多线程方法二:

复制代码
 1 package com.xun;
 2 
 3 //创建线程方式2:实现 runable 接口,重写run方法,实行线程需要丢入 runable 接口实现类,调用start 方法
 4 
 5 public class TestThread3 implements Runnable{
 6     @Override
 7     public void run(){
 8         //run 方法调用线程体
 9         for (int i = 0; i < 20; i++) {
10             System.out.println("runing...");
11         }
12     }
13 
14     public static void main(String[] args) {
15         //main 线程,即主线程
16 
17         TestThread3 tt3 = new TestThread3();
18 
19         //创建线程对象,通过线程对象来开启我们的线程
20 //        Thread thread = new Thread(tt3);
21 //        tt3.start();
22 
23         new Thread(tt3).start();
24         for (int i = 0; i < 200; i++) {
25             System.out.println("main....." + i);
26         }
27 
28     }
29 }
复制代码

小结:

  继承Thread类:

    子类继承Thread类具备多线程能力

    启动线程:子类对象.start()

    不建议使用:避免OOP单继承局限性

  实现Runable接口

    实现接口Runable具有多线程能力

    启动线程:传入目标对象+Thread对象.start()

    推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

   实例,初识并发

复制代码
 1 package com.xun;
 2 
 3 //发现问题,多个线程操作同一个资源,线程不安全,数据紊乱,并发问题
 4 public class TestThread4 implements Runnable{
 5 
 6     private int ticketNums = 10; //票数
 7 
 8     @Override
 9     public void run(){
10         while(ticketNums>0){
11             try {
12                 Thread.sleep(200);
13             } catch (InterruptedException e) {
14                 e.printStackTrace();
15             }
16             System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票");
17         }
18     }
19 
20     public static void main(String[] args) {
21         TestThread4 tt4 = new TestThread4();
22 
23         new Thread(tt4,"张三").start();
24         new Thread(tt4,"李四").start();
25         new Thread(tt4,"王五").start();
26     }
27 }
复制代码

   实例:龟兔赛跑,一个对象,多个线程使用

复制代码
 1 package com.xun;
 2 
 3 //发现问题,多个线程操作同一个资源,线程不安全,数据紊乱,并发问题
 4 public class TestThread4 implements Runnable{
 5 
 6     private int ticketNums = 10; //票数
 7 
 8     @Override
 9     public void run(){
10         while(ticketNums>0){
11             try {
12                 Thread.sleep(200);
13             } catch (InterruptedException e) {
14                 e.printStackTrace();
15             }
16             System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票");
17         }
18     }
19 
20     public static void main(String[] args) {
21         TestThread4 tt4 = new TestThread4();
22 
23         new Thread(tt4,"张三").start();
24         new Thread(tt4,"李四").start();
25         new Thread(tt4,"王五").start();
26     }
27 }
View Code
复制代码

   通过静态代理了解线程实现原理

复制代码
 1 package com.xun;
 2 //通过静态代理了解线程底部实现原理
 3 
 4 //静态代理模式总结
 5 //真实对象和代理对象都要实现同一个接口
 6 //代理对象要代理真实对象
 7     //好处:
 8         //代理对象可以做很多真实对象做不了的事
 9         //真实对象专注做自己的事
10 public class StaticProxy {
11 
12     public static void main(String[] args) {
13         You you = new You();
14 
15         new Thread(()-> System.out.println("I love you!")).start();
16 
17         new WeddingCompany(you).HappyMarry();
18 
19         //you.HappyMarry();
20 
21        // WeddingCompany weddingCompany = new WeddingCompany(you);
22         //weddingCompany.HappyMarry();
23     }
24 
25 }
26 
27 interface Marry{
28     void HappyMarry();
29 }
30 
31 //真实角色,你要结婚
32 class You implements Marry{
33     @Override
34     public void HappyMarry(){
35         System.out.println("你要开心的结婚了!");
36     }
37 }
38 
39 //代理角色,帮你结婚
40 class WeddingCompany implements Marry{
41 
42     private Marry target;
43 
44     public WeddingCompany(Marry target){
45         this.target=target;
46     }
47 
48     @Override
49     public void HappyMarry(){
50         before();
51         this.target.HappyMarry();
52         after();
53     }
54 
55     private void before(){
56         System.out.println("结婚前布置");
57     }
58 
59     private void after(){
60         System.out.println("婚后结尾款");
61     }
62 }
静态代理模式
复制代码

函数式接口Functional Interface

  定义:

    任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口

    对于函数式接口,我们可以通过lambda表达式来创建接口的对象

Lamda表达式  //JDK8新增特性

  意义:

    避免匿名内部类定义过多

    可以让你的代码看起来很简洁

    去掉了一堆没有意义的代码,只留下核心逻辑

复制代码
 1 package com.xun.lambda;
 2 
 3 public class TestLambda1 {
 4 
 5     //3、静态内部类
 6     static class Like2 implements ILike{
 7 
 8         @Override
 9         public void Lambda() {
10             System.out.println("I like lambda2");
11         }
12     }
13     public static void main(String[] args) {
14         ILike like = new Like1();
15         like.Lambda();
16 
17         like = new Like2();
18         like.Lambda();
19 
20         //4、局部内部类
21         class Like3 implements ILike{
22 
23             @Override
24             public void Lambda() {
25                 System.out.println("I like lambda3");
26             }
27         }
28 
29         like = new Like3();
30         like.Lambda();
31 
32         //5、匿名内部类
33         like = new ILike() {
34             @Override
35             public void Lambda() {
36                 System.out.println("I like lambda4");
37             }
38         };
39         like.Lambda();
40 
41         //6、用lambda简化
42         like = ()->{
43             System.out.println("I like lambda5");
44         };
45         like.Lambda();
46     }
47 }
48 
49 //1、定义一个函数式接口
50 interface ILike{
51     void Lambda();
52 }
53 
54 //2、实现类
55 class Like1 implements ILike{
56 
57     @Override
58     public void Lambda() {
59         System.out.println("I like lambda1");
60     }
61 }
lambda演化过程
复制代码

线程方法:

方法 说明
setPriority(int new Priority) 更改线程的优先级
static void sleep(long millis) 在指定毫秒数内让当前正在执行的线程进入休眠
void join() 等待该线程终止(插队)
static void yield() 暂停当前正在执行的线程对象,并执行其他线程
void interrupt() 中断线程(一般不用)
boolean isAlive() 测试线程是否处于活动状态

线程停止:

复制代码
 1 package com.xun.state;
 2 
 3 public class TestStop implements Runnable{
 4 
 5     //设置标志位
 6     private boolean flag = true;
 7 
 8     @Override
 9     public void run() {
10         int i=0;
11         while(flag){
12             System.out.println("runing..."+i++);
13         }
14 
15     }
16 
17     private void stop(){
18         this.flag = false;
19     }
20 
21     public static void main(String[] args) {
22         TestStop testStop = new TestStop();
23         new Thread(testStop).start();
24 
25         for(int i=0;i<1000;i++){
26             System.out.println("main..."+i);
27             if(i==900){
28                 //调用自己的stop方法
29                 testStop.stop();
30                 System.out.println("线程该停止了");
31             }
32         }
33     }
34 }
标志位线程停止
复制代码

 

线程休眠:

  sleep 存在异常InterruptedException;

  可以模拟网络延时;

  每个对象都有一把锁,sleep不会释放锁

 

线程礼让(yield)

  让当前执行线程暂停,但不阻塞,从运行态变为就绪态

  让cpu重写调度,礼让不一定成功

 

线程强制执行(join)

  join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞

  可以想象成插队

复制代码
 1 package com.xun.state;
 2 
 3 public class TestJoin implements Runnable{
 4 
 5     @Override
 6     public void run() {
 7         for (int i = 0; i < 1000; i++) {
 8             System.out.println("vip runing..."+i);
 9         }
10     }
11 
12     public static void main(String[] args) throws InterruptedException {
13         TestJoin testJoin = new TestJoin();
14         Thread thread = new Thread(testJoin);
15         thread.start();
16 
17         for (int i = 0; i < 200; i++) {
18             System.out.println("main ..."+i);
19             if(i==50){
20                 System.out.println("vip coming ");
21                 thread.join();
22             }
23         }
24     }
25 }
join
复制代码

 

线程观察状态:

复制代码
package com.xun.state;

public class TestState {
    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){//只要线程没终止就一直执行
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            state = thread.getState();
            System.out.println(state);

        }

    }
}
线程状态观察
复制代码

  线程中断或结束后,一旦进入死亡状态,就不能再启动了。

 

线程优先级(priority)

  1~10; get、set 方法。main方法默认优先级为5

  优先级低只表示调度概率低,并非绝对,主要看cpu调度

 

守护(daemon)线程

  线程分为用户线程和守护线程

  虚拟机必须确保用户线程执行完毕

  虚拟机不用等待守护线程执行完毕

  如:后台记录操作日志,监控内存,垃圾回收等待

复制代码
 1 package com.xun.state;
 2 
 3 public class TestDaemon {
 4     public static void main(String[] args) {
 5         God god = new God();
 6         You you = new You();
 7         Thread thread = new Thread(god);
 8         thread.setDaemon(true); //设置为守护线程
 9         thread.start();
10 
11         new Thread(you).start();
12     }
13 }
14 
15 class God implements Runnable{
16 
17     @Override
18     public void run() {
19         while(true){
20             System.out.println("god is alive");
21         }
22     }
23 }
24 
25 class You implements Runnable{
26 
27     @Override
28     public void run() {
29         for (int i = 0; i < 36500; i++) {
30             System.out.println("you are alive ");
31         }
32         System.out.println("GoodBye,World!");
33     }
34 }
守护线程
复制代码

 

线程同步

  同一个资源,多个人想使用,最天然的解决办法就是排队,一个一个来

  处理多线程问题是,多个线程访问同一个对象(并发),并且某些线程想修改这个对象。这时候我们就需要线程同步。线程同步其实就是一个等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。

  形成条件:队列 + 锁

  由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题,为了保证数据在方法中被访问时的正确性,在访问时加入 锁机制 synchronized,当一个线程获得对象的排他锁,独占资源,其他线程必须等待使用后释放锁即可。存在以下问题:

    一个线程持有锁会导致其他所有需要此锁的线程挂起;

    在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题;

    如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题。

  线程不安全的3个例子

复制代码
 1 package com.xun.syn;
 2 
 3 //不安全的买票
 4 //有重票和负数
 5 public class UnSafeBuyTicket {
 6     public static void main(String[] args) {
 7         BuyTicket buyTicket = new BuyTicket();
 8 
 9         new Thread(buyTicket,"ant").start();
10         new Thread(buyTicket,"bord").start();
11         new Thread(buyTicket,"cat").start();
12 
13     }
14 
15 }
16 
17 class BuyTicket implements Runnable{
18 
19     //
20     private int ticketNum = 10;
21     //外部停止方式
22     private boolean flag = true;
23 
24     @Override
25     public void run() {
26         //买票
27         while(flag){
28             buy();
29         }
30 
31     }
32     private void buy(){
33         if(ticketNum<=0){
34             flag=false;
35             return ;
36         }
37         //模拟延时
38         try {
39             Thread.sleep(100);
40         } catch (InterruptedException e) {
41             e.printStackTrace();
42         }
43         System.out.println(Thread.currentThread().getName()+"拿到票"+ticketNum--);
44     }
45 }
UnSafeBuyTicket
复制代码
复制代码
 1 package com.xun.syn;
 2 
 3 public class UnSafeBank {
 4     public static void main(String[] args) {
 5         Account account = new Account(100,"Marry money");
 6 
 7         Drawing you = new Drawing(50,"you",account);
 8         Drawing gril = new Drawing(100,"gril",account);
 9 
10         you.start();
11         gril.start();
12     }
13 }
14 
15 //账户
16 class Account{
17     String name;
18     int money;
19 
20     public Account(int money,String name){
21         this.money = money;
22         this.name = name;
23     }
24 }
25 
26 //银行
27 class Drawing extends Thread{
28     Account account;
29     int drawingMoney;
30     int nowMoney;
31 
32     public Drawing(int drawingMoney,String name,Account account){
33         super(name);
34         this.account=account;
35         this.drawingMoney=drawingMoney;
36     }
37 
38     //取钱
39     @Override
40     public void run(){
41         //判断有没有钱
42         if(account.money-drawingMoney<0){
43             System.out.println(Thread.currentThread().getName()+"钱不够,取不了");
44             return;
45         }
46         try {
47             Thread.sleep(100);
48         } catch (InterruptedException e) {
49             e.printStackTrace();
50         }
51         account.money-=drawingMoney;
52         nowMoney+=drawingMoney;
53 
54         System.out.println(account.name+"余额为"+account.money);
55         //Thread.currentThread().getName()等价于this.getName(),因为继承自Thread
56         System.out.println(this.getName()+"手里的钱"+nowMoney);
57     }
58 }
UnSafeBank
复制代码
复制代码
package com.xun.syn;

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 < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}
UnSafeList
复制代码

 

posted @   xunzf  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示