zoukankan      html  css  js  c++  java
  • 【java学习笔记】线程

    1.线程的定义

    ①继承Thread类,将执行的任务逻辑放到run方法中,调用start方法来开启线程

     1 public class ThreadDemo {
     2     public static void main(String[] args) {
     3         TDemo t = new TDemo();
     4         // 开启线程
     5         t.start();
     6         for (int i = 0; i < 20; i++) {
     7             System.out.println("main:" + i);
     8         }
     9     }
    10 }
    11 
    12 class TDemo extends Thread {
    13     @Override
    14     public void run() {
    15         for (int i = 0; i < 9; i++) {
    16             System.out.println("Thread:" + i);
    17         }
    18     }
    19 }

    ②实现Runnable,重写run方法,需要利用Runnable对象来构建一个Thread对象从而启动线程

        由于java是单继承的,因此当一个类已经继承了父类时,便不能继承Thread类。而又希望启用线程,此时实现Runnable接口即可达到目的。

     1 public class RunnableDemo {
     2     public static void main(String[] args) {
     3         RDemo r = new RDemo();
     4         // 通过Runnable对象来构建一个Thread对象
     5         Thread t = new Thread(r);
     6         t.start();
     7         for (int i = 0; i < 20; i++) {
     8             System.out.println("main:" + i);
     9         }
    10     }
    11 }
    12 
    13 class RDemo implements Runnable {
    14     @Override
    15     public void run() {
    16         for (int i = 0; i < 10; i++) {
    17             System.out.println("Thread:" + i);
    18         }
    19     }
    20 }

    ③实现Callable<T>,重写call方法

     1 import java.util.concurrent.Callable;
     2 import java.util.concurrent.ExecutionException;
     3 import java.util.concurrent.ExecutorService;
     4 import java.util.concurrent.Executors;
     5 import java.util.concurrent.Future;
     6 
     7 public class CallableDemo {
     8     public static void main(String[] args) throws InterruptedException, ExecutionException {
     9         ExecutorService es = Executors.newCachedThreadPool();
    10         Future<String> f = es.submit(new CDemo());
    11         System.out.println(f.get());
    12     }
    13 }
    14 
    15 class CDemo implements Callable<String> {
    16     @Override
    17     public String call() throws Exception {
    18         return "hahah~~~";
    19     }
    20 }

    2.线程的状态

        创建:新创建了一个线程对象。

        就绪:线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的执行权。

        运行:就绪状态的线程获取了CPU执行权,执行程序代码。

        阻塞: 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。

        死亡:线程执行完它的任务时。

    3.常见的线程方法

        ① Thread(String name)        初始化线程的名字

        ② getName()                        返回线程的名字

        ③ setName(String name)     设置线程对象名

        ④ sleep()                              线程睡眠指定的毫秒数。

        ⑤ getPriority()                      返回当前线程对象的优先级   默认线程的优先级是5

        ⑥ setPriority(int newPriority)     设置线程的优先级。(最大的优先级是10,最小的1,默认是5)虽然设置了线程的优先级,但是具体的实现取决于底层的操作系统的实现。

        ⑦ currentThread()               返回CPU正在执行的线程的对象。

    4.多线程的并发安全问题

        线程的执行不存在先后,相互抢占执行,抢占并不是只发生在线程执行的开始,而是发生在线程执行的每一步过程中。由于多个线程并发导致出现了一些不符合常理的数据的现象,即线程安全问题。

    4.1出现线程安全的根本原因

            ①存在两个或者两个以上的线程对象共享同一个资源
            ②多线程操作共享资源的代码有多句

    4.2线程安全问题的解决方案
    1.可以使用同步代码块去解决。

    1 synchronized(锁对象){
    2     需要被同步的代码
    3 }

         注意事项:
                ①锁对象可以是任意一个对象
                ②一个线程在同步块中sleep,并不会释放锁对象
                ③如果不存在线程安全问题,千万不要使用同步代码块,因为会降低效率
                ④锁对象必须是多线程共享的一个资源,否则锁不住


    2.同步函数 就是使用synchronized修饰的方法


         注意事项:
                ①如果是一个非静态的同步函数,锁对象是this;如果是静态的同步函数,锁对象是当前函数所属的类的字节码文件(class)
                ②同步函数的锁对象是固定的,不能由开发者来指定

    推荐使用同步代码块
        ①同步代码块的锁对象可以由开发者指定,方便控制。而同步方法是固定的。
        ②同步代码块可以很方便控制需要被同步代码的范围,同步函数必须是整个函数的所有代码都被同步

    例:模拟取款,

     1 public class Bank {
     2     public static void main(String[] args) {
     3         //创建一个账户
     4         Account account = new Account("10086", 1000);
     5         //模拟两个线程对同一个账户取钱
     6         new DrawThread("张三",account,800).start();
     7         new DrawThread("李四", account, 900).start();
     8     }
     9 }
    10 
    11 class DrawThread extends Thread{
    12     //模拟用户账户
    13     private Account account;
    14     //当前取钱线程所希望取得钱数
    15     private double drawMoney;
    16 
    17     public DrawThread(String name, Account account, double drawMoney) {
    18         super(name);
    19         this.account = account;
    20         this.drawMoney = drawMoney;
    21     }
    22 
    23     @Override
    24     public void run() {
    25         /*
    26          * 虽然java程序允许任何对象作为同步监视器,但是同步监视器的目的:
    27          * 阻止两个线程对同一个共享资源进行并发访问,因此推荐使用可能被并发访问的共享资源
    28          * 当做同步监视器
    29          * 
    30          * 加锁-修改-释放锁
    31          * 
    32          * 字节码文件
    33          * 静态变量
    34          * "锁对象"
    35          * 
    36          */
    37         synchronized (account) {
    38                 //synchronized (this) {              //非共享,失败
    39         //synchronized (new String("")) {  //非共享,失败
    40         //synchronized ("") {              
    41             //账户余额大于取钱数目
    42             if(account.getBalance() >= drawMoney) {
    43                 //吐出钞票
    44                 System.out.println(getName() + "取钱成功!吐出钞票:" + drawMoney);
    45 
    46 //                try {
    47 //                    Thread.sleep(5000);
    48 //                } catch (Exception e) {
    49 //                    e.printStackTrace();
    50 //                }
    51 
    52                 //修改余额
    53                 account.setBalance(account.getBalance() - drawMoney);
    54                 System.out.println("	余额为:" + account.getBalance());
    55             } else {
    56                 System.out.println(getName() + "取钱失败!余额不足!");
    57             }    
    58         }
    59     }
    60 }
    61 
    62 class Account{
    63     //封装账户编号、账户余额两个成员变量
    64     private String accountNo;
    65     private double balance;
    66     
    67     public Account(String accountNo, double balance) {
    68         super();
    69         this.accountNo = accountNo;
    70         this.balance = balance;
    71     }
    72 
    73     public String getAccountNo() {
    74         return accountNo;
    75     }
    76 
    77     public void setAccountNo(String accountNo) {
    78         this.accountNo = accountNo;
    79     }
    80 
    81     public double getBalance() {
    82         return balance;
    83     }
    84 
    85     public void setBalance(double balance) {
    86         this.balance = balance;
    87     }
    88 }
    synchronized代码块Demo
     1 public class Bank2 {
     2     public static void main(String[] args) {        
     3         //创建一个账户
     4         Account2 account = new Account2("10086", 1000);
     5         //模拟两个线程对同一个账户取钱
     6         new DrawThread2("张三",account,800).start();
     7         new DrawThread2("李四", account, 900).start();
     8     }
     9 }
    10 
    11 class DrawThread2 extends Thread{
    12     //模拟用户账户
    13     private Account2 account;
    14     //当前取钱线程所希望取得钱数
    15     private double drawMoney;
    16     
    17     public DrawThread2(String name, Account2 account, double drawMoney) {
    18         super(name);
    19         this.account = account;
    20         this.drawMoney = drawMoney;
    21     }
    22 
    23     @Override
    24     public void run() {
    25         account.draw(drawMoney);
    26         
    27     }
    28 }
    29 
    30 class Account2{
    31     //封装账户编号、账户余额两个成员变量
    32     private String accountNo;
    33     private double balance;
    34     
    35     public Account2(String accountNo, double balance) {
    36         super();
    37         this.accountNo = accountNo;
    38         this.balance = balance;
    39     }
    40 
    41     public String getAccountNo() {
    42         return accountNo;
    43     }
    44 
    45     public void setAccountNo(String accountNo) {
    46         this.accountNo = accountNo;
    47     }
    48 
    49     public double getBalance() {
    50         return balance;
    51     }
    52     
    53     /*
    54      * 锁对象是this
    55      * 对于同一个Account账户而言,任意时刻只能有一个线程获得对account对象的锁定
    56      */
    57     public synchronized void draw(double drawMoney) {
    58         //账户余额大于取钱数目
    59         if(balance >= drawMoney) {
    60             //吐出钞票
    61             System.out.println(Thread.currentThread().getName() + "取钱成功!吐出钞票:" + drawMoney);
    62             
    63         try {
    64             Thread.sleep(5000);
    65         } catch (Exception e) {
    66             e.printStackTrace();
    67         }
    68             
    69             //修改余额
    70             balance -= drawMoney;
    71             System.out.println("	余额为:" + balance);
    72         } else {
    73             System.out.println(Thread.currentThread().getName() + "取钱失败!余额不足!");
    74         }    
    75     }
    76 
    77 }
    synchronized方法Demo

    成功的情况:                                             

      

    失败的情况:

      

    5.死锁

        由于多个线程之间的锁形成了嵌套导致程序无法继续运行的现象。

        避免死锁:减少线程数量,统一锁对象,减少锁嵌套。

     1 public class DeadLockDemo {
     2     public static void main(String[] args) {
     3         new Thread(new Runnable() { // 创建线程
     4             public void run() {
     5                 synchronized ("资源1") { 
     6                     System.out.println(Thread.currentThread().getName() + ":得不到资源2,就不释放资源1");
     7                     try {
     8                         Thread.sleep(10);
     9                     } catch (InterruptedException e) {
    10                         e.printStackTrace();
    11                     }
    12                     synchronized ("资源2") {
    13                         System.out.println(Thread.currentThread().getName() + ":得到资源2,释放资源1");
    14                     }
    15                 }
    16             }
    17         }, "线程A").start();
    18         new Thread(new Runnable() { // 美国人
    19             public void run() {
    20                 synchronized ("资源2") { // 美国人拿到了筷子
    21                     System.out.println(Thread.currentThread().getName() + ":得不到资源1,就不释放资源2");
    22                     try {
    23                         Thread.sleep(10);
    24                     } catch (InterruptedException e) {
    25                         e.printStackTrace();
    26                     }
    27                     synchronized ("资源1") {
    28                         System.out.println(Thread.currentThread().getName() + ":得到资源1,释放资源2");
    29                     }
    30                 }
    31             }
    32         }, "线程B").start();
    33     }
    34 }
    DeadLockDemo

    死锁:

      

    正常:

      

     6.等待唤醒机制

      wait:告诉当前线程放弃执行权,并放弃监视器(锁)并进入阻塞状态,直到其他线程持有获得执行权,并持有了相同的监视器(锁)并调用notify为止。

      notify:唤醒持有同一个监视器(锁)中调用wait的第一个线程,例如,餐馆有空位置后,等候就餐最久的顾客最先入座。注意:被唤醒的线程是进入了可运行状态。等待cpu执行权。

      notifyAll:唤醒持有同一监视器中调用wait的所有的线程。

    ①notify

    通过等待唤醒机制调节了线程之间的执行顺序

     1 public class WaitNotifyDemo {
     2     public static void main(String[] args) {
     3         Student s = new Student();
     4         s.setName("Tom");
     5         s.setGender('男');
     6         new Thread(new Ask(s)).start();
     7         new Thread(new Change(s)).start();
     8     }
     9 }
    10 
    11 class Change implements Runnable {
    12     private Student s;
    13     public Change(Student s) {
    14         this.s = s;
    15     }
    16     
    17     @Override
    18     public void run() {
    19         while (true) {
    20             synchronized (s) {
    21                 if (s.flag)
    22                     try {
    23                         s.wait();
    24                     } catch (InterruptedException e) {
    25                         e.printStackTrace();
    26                     }
    27 
    28                 if (s.getGender() == '男') {
    29                     s.setName("Amy");
    30                     s.setGender('女');
    31                 } else {
    32                     s.setName("Tom");
    33                     s.setGender('男');
    34                 }
    35                 s.flag = true;
    36                 // 唤醒在等待的线程
    37                 s.notify();
    38             }
    39         }
    40     }
    41 }
    42 
    43 class Ask implements Runnable {
    44     private Student s;
    45     public Ask(Student s) {
    46         this.s = s;
    47     }
    48 
    49     @Override
    50     public void run() {
    51         while (true) {
    52             synchronized (s) {
    53                 if (!s.flag)
    54                     try {
    55                         s.wait();
    56                     } catch (InterruptedException e) {
    57                         e.printStackTrace();
    58                     }
    59                 System.out.println("我是" + s.getName() + ",我是" + s.getGender());
    60                 s.flag = false;
    61                 s.notify();
    62             }
    63         }
    64     }
    65 }
    66 
    67 class Student {
    68 
    69     private String name;
    70     private char gender;
    71     // 标记位---规定flag为true,执行ask线程,如果flag为false执行change
    72     public boolean flag = true;
    73 
    74     public String getName() {
    75         return name;
    76     }
    77 
    78     public void setName(String name) {
    79         this.name = name;
    80     }
    81 
    82     public char getGender() {
    83         return gender;
    84     }
    85 
    86     public void setGender(char gender) {
    87         this.gender = gender;
    88     }
    89 
    90 }
    WaitNotifyDemo

    ②notifyAll

    线程在等待期间是在这个锁所对应的线程池中等待的。线程池本质上是一个存储线程的队列。

    若是用notify,唤醒队列中的第一个线程,执行方式如下:

    第一个括号代表就绪的线程,第二个括号代表线程池中的线程。

    (a1,a2,c1,c2)() -> a1 running-> (a1,a2,c1,c2)() -> a1 running -> (a2,c1,c2)(a1) -> a2 running -> (c1,c2)(a1,a2) -> c1 running -> (a1,c1,c2)(a2)  -> c1 running -> (a1,c2)(a2,c1) -> c2 running -> (a1)(a2,c1,c2) -> a1 runnig -> (a1,a2)(c1,c2) -> a1 running -> (a2)(c1,c2,a1) -> a2 running -> ()(c1,c2,a1,a2)

    最后,所有线程都在线程池中阻塞,发送死锁。此时需要用到notifyAll。

     1 public class WaitNotifyAllDemo {
     2     public static void main(String[] args) {
     3         Student s = new Student();
     4         s.setName("Tom");
     5         s.setGender('男');
     6         new Thread(new Ask2(s)).start();
     7         new Thread(new Ask2(s)).start();
     8         new Thread(new Change2(s)).start();
     9         new Thread(new Change2(s)).start();
    10     }
    11 }
    12 class Change2 implements Runnable {
    13     private Student s;
    14     public Change2(Student s) {
    15         this.s = s;
    16     }
    17 
    18     @Override
    19     public void run() {
    20         while (true) {
    21             synchronized (s) {
    22                 while (s.flag)
    23                     try {
    24                         s.wait();
    25                     } catch (InterruptedException e) {
    26                         e.printStackTrace();
    27                     }
    28 
    29                 if (s.getGender() == '男') {
    30                     s.setName("Amy");
    31                     s.setGender('女');
    32                 } else {
    33                     s.setName("Tom");
    34                     s.setGender('男');
    35                 }
    36                 s.flag = true;
    37                 // 唤醒在等待的线程
    38                 s.notifyAll();
    39             }
    40         }
    41     }
    42 }
    43 
    44 class Ask2 implements Runnable {
    45     private Student s;
    46     public Ask2(Student s) {
    47         this.s = s;
    48     }
    49     @Override
    50     public void run() {
    51 
    52         while (true) {
    53             synchronized (s) {
    54 
    55                 while (!s.flag)
    56                     try {
    57                         s.wait();
    58                     } catch (InterruptedException e) {
    59                         e.printStackTrace();
    60                     }
    61 
    62                 System.out.println("我是" + s.getName() + ",我是" + s.getGender());
    63                 s.flag = false;
    64                 s.notifyAll();
    65             }
    66         }
    67     }
    68 }
    WaitNotifyAllDemo

    sleep与wait的区别:

        ①sleep在使用的时候需要指定休眠时间,到点自然醒。释放执行权,不释放锁。是一个静态方法,设计在了Thread类上

        ②wait在使用的时候可以指定等待时间,也可以不指定,如果不指定等待时间就需要唤醒。释放执行权,释放锁。是一个非静态方法,设计在了Object类上

       

    例:生产消费模型

        一个线程表示生产者Producer,一个线程表示消费者Consumer,商品的总数量不超过1000。

      1 public class ProductAndConsumer {
      2 
      3     public static void main(String[] args) {
      4 
      5         Product p = new Product();
      6 
      7         new Thread(new Producer(p)).start();
      8         new Thread(new Consumer(p)).start();
      9 
     10     }
     11 
     12 }
     13 
     14 class Producer implements Runnable {
     15 
     16     private Product p;
     17 
     18     public Producer(Product p) {
     19         this.p = p;
     20     }
     21 
     22     @Override
     23     public void run() {
     24 
     25         while (true) {
     26 
     27             synchronized (p) {
     28 
     29                 while (p.flag) {
     30                     try {
     31                         p.wait();
     32                     } catch (InterruptedException e) {
     33                         e.printStackTrace();
     34                     }
     35                 }
     36 
     37                 // 计算本次商品所能生产的最大数量
     38                 int max = 1000 - p.getCount();
     39 
     40                 // 计算本次生产的商品数量
     41                 int count = (int) (Math.random() * (max+1));
     42 
     43                 // 本次提供的商品的总数量
     44                 p.setCount(p.getCount() + count);
     45 
     46                 System.out.println("本次生产数量:" + count + ", 本次提供商品数量为:" + p.getCount());
     47 
     48                 p.flag = true;
     49                 p.notify();
     50 
     51             }
     52 
     53         }
     54     }
     55 
     56 }
     57 
     58 class Consumer implements Runnable {
     59 
     60     private Product p;
     61 
     62     public Consumer(Product p) {
     63         this.p = p;
     64     }
     65 
     66     @Override
     67     public void run() {
     68 
     69         while (true) {
     70             synchronized (p) {
     71 
     72                 while (!p.flag) {
     73                     try {
     74                         p.wait();
     75                     } catch (InterruptedException e) {
     76                         e.printStackTrace();
     77                     }
     78                 }
     79 
     80                 // 计算本次消费的数量
     81                 int count = (int) (Math.random() * (p.getCount() + 1));
     82 
     83                 // 计算本次的剩余数量
     84                 p.setCount(p.getCount() - count);
     85 
     86                 System.out.println("本次消费数量:" + count + ", 本次剩余商品数量:" + p.getCount());
     87 
     88                 p.flag = false;
     89                 p.notify();
     90             }
     91         }
     92 
     93     }
     94 
     95 }
     96 
     97 class Product {
     98 
     99     private int count;
    100     public boolean flag = false;
    101 
    102     public int getCount() {
    103         return count;
    104     }
    105 
    106     public void setCount(int count) {
    107         this.count = count;
    108     }
    109 
    110 }
    ProductAndConsumer

    7.守护线程

        守护别的线程。当被守护的线程结束,守护线程无论执行完成与否都得随之结束。

        一个线程要么是守护线程要么是被守护的线程。守护线程是随着最后一个被守护线程的结束而结束,例如GC。

     1 public class DaemonDemo {
     2 
     3     public static void main(String[] args) throws InterruptedException {
     4 
     5         Thread t1 = new Thread(new Soilder(), "小兵1号");
     6         Thread t2 = new Thread(new Soilder(), "小兵2号");
     7         Thread t3 = new Thread(new Soilder(), "小兵3号");
     8         Thread t4 = new Thread(new Soilder(), "小兵4号");
     9 
    10         // 设置为守护线程
    11         t1.setDaemon(true);
    12         t2.setDaemon(true);
    13         t3.setDaemon(true);
    14         t4.setDaemon(true);
    15 
    16         t1.start();
    17         t2.start();
    18         t3.start();
    19         t4.start();
    20 
    21         for (int i = 10; i > 0; i--) {
    22             System.out.println("Boss掉了一滴血,剩余" + i);
    23             Thread.sleep(150);
    24         }
    25     }
    26 
    27 }
    28 
    29 class Soilder implements Runnable {
    30 
    31     @Override
    32     public void run() {
    33 
    34         for (int i = 100; i > 0; i--) {
    35             System.out.println(Thread.currentThread().getName() + "掉了一滴血,剩余" + i);
    36             try {
    37                 Thread.sleep(10);
    38             } catch (InterruptedException e) {
    39                 e.printStackTrace();
    40             }
    41         }
    42 
    43     }
    44 
    45 }
    DaemonDemo

    8.线程的优先级

        线程的优先级分为1-10,理论上数字越大等级越高,这个线程抢到资源的几率就越大。相邻的两个线程的优先级的差异性不明显。至少要相差5个等级才能体现的相对明显一点点。

     1 public class PriorityDemo {
     2     public static void main(String[] args) {
     3 
     4         Thread t1 = new Thread(new PDemo(), "A");
     5         Thread t2 = new Thread(new PDemo(), "B");
     6 
     7         t1.setPriority(1);
     8         t2.setPriority(10);
     9 
    10         t1.start();
    11         t2.start();
    12 
    13         // 获取线程的优先级
    14         // System.out.println(t1.getPriority());
    15         // System.out.println(t2.getPriority());
    16 
    17     }
    18 }
    19 
    20 class PDemo implements Runnable {
    21     @Override
    22     public void run() {
    23         for (int i = 0; i < 10; i++) {
    24             System.out.println(Thread.currentThread().getName() + ":" + i);
    25         }
    26     }
    27 }
    PriorityDemo
  • 相关阅读:
    luoguP2016 战略游戏
    [Usaco2006 Nov]Corn Fields牧场的安排
    [Ahoi2009]self 同类分布
    POJ3208:Apocalypse Someday
    [usaco2010 Oct]Soda Machine
    [Usaco2005 Dec]Scales 天平
    PTA的Python练习题(十九)
    堆叠注入
    PHP序列化与反序列化(三)总结实战
    攻防世界web进阶1-12总结篇
  • 原文地址:https://www.cnblogs.com/kuotian/p/8459888.html
Copyright © 2011-2022 走看看