线程

线程
2019-11-1414:44:18
 
/**
 * 程序 : 保存在硬盘上的一段可执行的静态代码的集合(是可执行文件)
 * 进程 : 一段正在内存中运行中的程序(有生命周期), 进程之间不可以直接互访.
 * 线程 : 一个进程中的多个子任务, 直接由CPU负责处理执行. 多线程之间可以方便的共享数据
 *
 * 线程启动第一种方式, 实现的方式
 * 1) 写一个具体类, 实现Runnable接口, 并实现接口中的抽象方法run, 这个方法就是线程体
 * 2) 创建这个具体类的对象, 并以它为实参再创建Thread类的对象, 就是线程对象
 * 3) 调用线程对象的start方法, 启动线程, 具体类中的run方法就会被调用执行.
 *
 * 线程启动第二种方式, 继承的方式
 * 1) 写一个具体类, 继承Thread类, 必须重写run方法, 因为父类中的run方法什么也没有做
 * 2) 创建这个具体类对象, 相当于创建了线程对象
 * 3) 调用此线程对象的start方法, 启动线程.
 *
 * 多线程程序的优点:
 提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
 提高计算机系统CPU的利用率
 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改
 
  生命周期 :
             阻塞(sleep, join, wait...) 
           |     |
           |  失去CPU执行权 |
 新建(new Thread()) -> 就绪(start()) <--------------- |      正常或异常结束run
          ---------------> 运行-----------------------> 死亡
          获得CPU执行权
 
 线程同步 : 多个线程 "同时" 修改 "同"一个数据
 
 */
class MyThread extends Thread {
 
 @Override
 public void run() {
  for (int i = 0; i < 100; i++) {
   System.out.println(Thread.currentThread().getName() + " : " + i);
  }
 }
}
public class ThreadTest {
 
 public static void main(String[] args) {
  Thread myThread = new MyThread(); // 相当于建栈
  myThread.start(); // 激活栈, 把run压入
  
  try {
   myThread.join(); // 主线程阻塞, 子线程执行, 子线程执行完, 主线程才继续.
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  
  for (int i = 0; i < 100; i++) {
   System.out.println(Thread.currentThread().getName() + " : " + i);
  }
 }
}
 

一、创建并启动一个线程的方法

  1)实现接口的方式

    ① 写一个具体类, 实现Runnable接口, 并实现其中的run方法,这个run方法就是线程的入口方法
          ② 创建这个类的对象, 并以这个对象为实参, 创建Thread类的对象
         ③ 调用Thread对象的start()方法 启动线程

public class ExerRunner implements Runnable {
    
    //1 创建一个子线程,在线程中输出1-100之间的偶数,主线程输出1-100之间的奇数。
    private int count = 1;

    @Override
    public void run() {
        for (; count < 100; count++) {
            if (count % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + " : " + count);
            }
        }
    }
}


public class ExerRunnerTest {
    
    public static void main(String[] args) {
        //1 创建一个子线程,在线程中输出1-100之间的偶数,主线程输出1-100之间的奇数。
        Runnable exerRunner = new ExerRunner();
        Thread thread = new Thread(exerRunner);
        thread.start();
        
        for (int i = 0; i < 100; i++) {
            if (i % 2 != 0) {
                System.out.println(Thread.currentThread().getName() + " : " + i);
            }
        }
    }
}

 二、同时 创建俩个 子线程

public class HelloRunner implements Runnable {
    
    private int i;

    @Override
    public void run() {
        for (i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "** " + i); // 哪个栈压入此方法, 就获取哪个栈
        }
    }
    
    
}


public class HelloRunner2 implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "## " + i);
        }
    }

}



public class HelloRunnerTest {
    
    public static void main(String[] args) {
        //  1) 写一个具体类, 实现Runnable接口, 并实现接口中的抽象方法run, 这个方法就是线程体
        //* 2) 创建这个具体类的对象, 并以它为实参再创建Thread类的对象, 就是线程对象
        Runnable runner = new HelloRunner();
        Thread thread = new Thread(runner); // 执行此操作时在内部创建一个栈.
        thread.setName("子线程");
        //* 3) 调用线程对象的start方法, 启动线程, 具体类中的run方法就会被调用执行.
        thread.start(); // 两个动作, 1) 激活栈, 2) 把run压入栈底, 马上结束返回
        //thread.run(); run只是普通方法调用
        
        Thread thread2 = new Thread(runner);
        thread2.start();
        
        //Thread.currentThread().setName("主线程");
        
        // 启动另一个子线程
        Runnable helloRunner2 = new HelloRunner2();
        Thread thread3 = new Thread(helloRunner2);
        thread3.start();
    }
}

 三、以通知的方式停止线程

public class StopRunner implements Runnable {

    private int count = 0;
    private boolean runFlag = true;
    
    public void setRunFlag(boolean runFlag) {
        this.runFlag = runFlag;
    }
    
    public boolean isRunFlag() {
        return runFlag;
    }
    
    //在main方法中创建并启动1个线程。线程循环随机打印100以内的整数,直到主线程从键盘读取了“Q”命令。

    @Override
    public void run() {
        while (runFlag) {
            System.out.println(Thread.currentThread().getName() + " : " + count++);
            if (count == 100) {
                count = 0;
            }
        }
        System.out.println("关键代码");
    }
    
}


public class StopRunnerTest {
    
    public static void main(String[] args) {
        Runnable stopRunner = new StopRunner();
        Thread thread = new Thread(stopRunner);
        thread.start();
        
        int j = 0;
        for (int i = 0; i < 10000000; i++) {
            j++;
        }
        
        //thread.stop(); // 这个方法禁止使用, 因为它太暴力了!!!!
                // 用 多态的方式
        ((StopRunner)stopRunner).setRunFlag(false); // 以通知的方式停止线程
        
        
    }
}
 

四、

//在main方法中创建并启动1个线程。线程循环随机打印100以内的整数,直到主线程从键盘读取了“Q”命令。
public class ExerStopRunner implements Runnable {
    
    private boolean runFlag = true;
    
    public void setRunFlag(boolean runFlag) {
        this.runFlag = runFlag;
    }
    
    @Override
    public void run() {
        while (runFlag) {
            System.out.println(Thread.currentThread().getName() + " : " + (int)(Math.random() * 100));
        }
        System.out.println("关键代码");
    }
    
    
}


public class ExerSleepRunnerTest {
    
    public static void main(String[] args) {
        Runnable runner = new ExerSleepRunner();
        Thread thread = new Thread(runner);
        thread.start();
        
        // 不断地观察loopFor的值
        while (true) {
            if (((ExerSleepRunner)runner).isLoopOver()) {
                break;
            }
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        thread.interrupt();
    }
}

五、睡眠的几种解除方式

 线程调用sleep方法后, 会使调用者线程进入睡眠状态, 不会抢夺CPU... 直到解除阻塞为止
 线程进入sleep后, 几种解除方式 :
   1) 时间到了, 自然醒

  2) 被别的线程打断, 调用本线程对象的interrupt(), 被打断的线程会抛出异常

 

public class SleepRunner implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            int n = (int)(Math.random() * 200);
            System.out.println(n);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                System.out.println("被打断了....");
            } 
        }
        System.out.println("for循环结束了");
        
        try {
            Thread.sleep(30 * 1000);
        } catch (InterruptedException e) {
            System.out.println("在睡30秒时被打断");
        }
        
        System.out.println("子线程OVER");
    }
    
}


public class SleepRunnerTest {
    
    public static void main(String[] args) {
        Runnable runner = new SleepRunner();
        Thread thread = new Thread(runner);
        thread.start();
        
        System.out.println("main方法");
        
        try {
            Thread.sleep(21 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        thread.interrupt();
        
    }
}

 

六、

/**编写程序,在main方法中创建一个线程。线程每隔一定时间(200ms以内的随机时间)产生一个0-100之间的随机整数,
打印后将该整数放到集合中;
共产生100个整数,全部产生后,睡眠30秒,然后将集合内容打印输出;
在main线程中,唤醒上述睡眠的线程,使其尽快打印集合内容。*/

 

public class ExerSleepRunner implements Runnable {
    
    private boolean loopOver = false;
    
    public boolean isLoopOver() {
        return loopOver;
    }
    
    @Override
    public void run() {
        List<Integer> list = new ArrayList<Integer>();
        for (int i = 0; i < 100; i++) {
            int n = (int)(Math.random() * 100);
            System.out.println(n);
            list.add(n);
            
            int randTime = (int)(Math.random() * 200);
            try {
                Thread.sleep(randTime);
            } catch (InterruptedException e) {
                System.out.println("在睡" + randTime + "毫秒时被打断");
            }
        }
        loopOver = true;
        
        System.out.println("我要睡30秒了.....");
        try {
            Thread.sleep(30 * 1000);
        } catch (InterruptedException e) {
            System.out.println("在长睡时被打断, 严重不爽");
        }
        
        System.out.println("我要打印集合了.....");
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()) {
            Integer next = iterator.next();
            System.out.println(next);
        }
    }
        
}



public class ExerSleepRunnerTest {
    
    public static void main(String[] args) {
        Runnable runner = new ExerSleepRunner();
        Thread thread = new Thread(runner);
        thread.start();
        
        // 不断地观察loopFor的值
        while (true) {
            if (((ExerSleepRunner)runner).isLoopOver()) {
                break;
            }
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        thread.interrupt();
    }
}

七、

*******

通过构造器 对象关联

必须在线程启动前设置守护线程

守护线程特点 : 如果没有了用户线程, 所有守护线程自动终止.

在main方法中创建并启动1个线程。线程循环随机打印100以内的整数,直到主线程从键盘读取了“Q”命令。

 

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class KeyListener implements Runnable {

    private Runnable runner;
    
    public KeyListener(Runnable runner) {
        super();
        this.runner = runner;
    }

    @Override
    public void run() {
        BufferedReader bufferedReader = null;
        try {
            bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            String line = bufferedReader.readLine();
            while (line != null) {
                // 处理数据
                if (line.equalsIgnoreCase("q")) {
                    // 停止子线程
                    ((ExerStopRunner)runner).setRunFlag(false);
                    //break; // 自我终结
                }
                // 继续读
                line = bufferedReader.readLine();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (Exception e2) {
                }
            }
        }
    }

}


public class ExerStopRunnerTest {
    
    public static void main(String[] args) {
        //在main方法中创建并启动1个线程。线程循环随机打印100以内的整
        Runnable runner = new ExerStopRunner();
        Thread thread = new Thread(runner);
        thread.start();
        
        //在main方法中创建并启动两个线程。第一个线程循环随机打印100以内的整数,直到第二个线程从键盘读取了“Q”命令。
        Runnable runner2 = new KeyListener(runner); // 通过构造器 对象关联
        Thread thread2 = new Thread(runner2);
        // 必须在线程启动前设置守护线程
        thread2.setDaemon(true); // 守护线程特点 : 如果没有了用户线程, 所有守护线程自动终止.
        thread2.start();
        
    } 
}

 

 八 、线程的互斥锁

public class Counter implements Runnable {
    
    private int counter = 200;
    private Object object = new Object();
    
    @Override
    //public synchronized void run() {// 粒度太大, 不好
    public void run() {
        //synchronized (this) { // 粒度太大, 不好
            for (int i = 0; i < 50; i++) {
                synchronized ("") { // 互斥锁对象, 下面的代码就会只在某一个线程中执行, 其他线程想执行下面的代码, 必须要等待.
                    // 具有原子性的代码, 具有不可分割性
                    counter -= 2;
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " : " + counter);
                }
            }
        //}
    }

}

public class CounterTest {
    
    public static void main(String[] args) {
        Runnable runner = new Counter();
        
        Thread thread1 = new Thread(runner);
        thread1.setName("子线程1");
        thread1.start();
        
        Thread thread2 = new Thread(runner);
        thread2.setName("子线程2");
        thread2.start();
    }
}

 

九、银行账户

 

银行有一个账户Account包含属性name, balance
写一个普通 类Deposit实现Runnable, 在run方法中存钱
有两个柜台(线程)分别同时向同一个账户存3000元,每次存1000,存3次。每次存完打印账户余额。睡眠10毫秒
问题:该程序是否有安全问题,如果有,如何解决?
扩展练习 :
一个柜台Deposit存3000元, 每次存1000,存3次
另一个柜台Withdraw取3000元, 每次取1000,取3次
【提示】
1,明确哪些代码是多线程运行代码,须写入run()方法
2,明确什么是共享数据。
3,明确多线程运行代码中哪些语句是操作共享数据的。
 
public class RunnerDeposit implements Runnable{
    private Account account;
    public RunnerDeposit(Account account) {
        this.account=account;
    }
    private double money;
    //Account account=new Account();
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            synchronized ("") {
                money = account.getBalance();
                account.setBalance(money+1000);
                //balance+=money;
                //余额
                System.out.println(Thread.currentThread().getName()+"刚刚存了1000元");
                //余额
                System.out.println("账户余额:"+account.getBalance()+"元");
                
                System.out.println();
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    System.out.println("存钱中,请勿打扰...!");
                }
            }
            
        }
        
    }
}

public class RunnerWithdraw implements Runnable{
    private Account account;
    public RunnerWithdraw(Account account) {
        this.account=account;
    }
    private double money;
    @Override
    public void run() {
        
        for (int i = 0; i < 3; i++) {
            synchronized ("") {
                money = account.getBalance();
                account.setBalance(money-1000);
                System.out.println(Thread.currentThread().getName()+"刚刚取了1000元");
                //余额
                System.out.println();
                System.out.println("账户余额:"+account.getBalance());
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    System.out.println("取钱中,请勿打扰...!");
                }
             }
            
        }
    }

}

public class RunnerAccountMain {
    public static void main(String[] args) {
         Account account = new Account("张三", 0);
        Runnable runnerDeposit = new RunnerDeposit(account);
        Thread account1 = new Thread(runnerDeposit);
        account1.setName("柜台1:");
        account1.start();
        
        Thread account2 = new Thread(runnerDeposit);
        account2.setName("柜台2:");
        account2.start();
        
        Runnable runnerWithdraw = new RunnerWithdraw(account);
        Thread account3 = new Thread(runnerWithdraw);
        account3.setName("柜台3:");
        
        account3.start();
        
        
    }
}

 

posted @ 2019-11-14 14:51  Hunter·Zhang  阅读(249)  评论(0编辑  收藏  举报