Java学习笔记—浅谈多线程
来源:互联网 发布:windows脚本编程格式 编辑:程序博客网 时间:2024/05/21 14:52
涉及的基类:主要涉及的基类是Java.lang.Thread类,该类实现了Runnable接口
一、Java多线程实现的第一种方式:继承Thread类
第一步:编写继承了Thread类的子类
第二步:子类中重写run()方法
第三步:创建子类
第四步:调用start()方法
如下例模拟了龟兔赛跑:
/** * 模拟龟兔赛跑 * 第一步:编写继承了Thread类的子类 * 第二步:子类中重写run方法 * */public class Rabbit extends Thread{ @Override public void run() { //线程体 for(int i=0;i<100;i++){ System.out.println("兔子跑了"+i+"步"); } }}
public class Test{ public static void main(String[] args) { // 第三步:创建子类对象 Rabbit rab = new Rabbit(); // 第四步:调用start()方法 rab.start();// 强调:不是调用run()方法 for(int i=0;i<100;i++){ System.out.println("乌龟跑了"+i+"步"); } }}
二、Java多线程实现的第二种方式:实现Runnable接口
主要应用了静态代理设计模式
第一步:编写实现了Runnable接口的子类
第二步:子类中重写run()方法
第三步:创建子类(相当于静态代理中创建真实角色)
第四部:创建Thread类,传入子类的引用(相当于静态代理中创建代理角色)
第五步:调用Thread类的start()方法
/** * 模拟龟兔赛跑 * 第一步:编写实现了Runnable接口的子类 * 第二步:子类中重写run()方法 * */public class Rabbit implements Runnable{ @Override public void run() { //线程体 for(int i=0;i<100;i++){ System.out.println("兔子跑了"+i+"步"); } }}
public class Test{ public static void main(String[] args) { // 第三步:创建子类(相当于静态代理中创建真实角色) Rabbit rab = new Rabbit(); // 第四步:创建Thread类,传入子类的引用(相当于静态代理中创建代理角色) Thread proxy = new Thread(rab); // 第五步:调用Thread类的start()方法 proxy.start(); for (int i = 0; i < 100; i++) { System.out.println("乌龟跑了" + i + "步"); } }}
三、线程的生命周期
- 新建:线程被new出来
- 准备就绪:线程具有执行的资格,即线程调用了start(),没有执行的权利
- 运行:具备执行的资格和具备执行的权利
- 阻塞:没有执行的资格和执行的权利
- 销毁:线程的对象变成垃圾,释放资源
四、线程的并发安全性问题
案例:模拟抢票,火车站有100张票,4个窗口同时卖票
public class Test { public static void main(String[] args) { // 创建线程 SaleTicket st1 = new SaleTicket(); SaleTicket st2 = new SaleTicket(); SaleTicket st3 = new SaleTicket(); SaleTicket st4 = new SaleTicket(); // 线程指定名称 st1.setName("窗口1"); st2.setName("窗口2"); st3.setName("窗口3"); st4.setName("窗口4"); // 线程启动 st1.start(); st2.start(); st3.start(); st4.start(); }}/** * 卖票的窗口 * * */class SaleTicket extends Thread { //总票数 private static int tickets = 100; @Override public void run() { while (true) { if (tickets > 0) { try { Thread.sleep(300); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } System.out.println(this.getName() + "正在卖" + tickets-- + "张票"); } else { System.out.println("票已经售完"); break; } } }}
可以观察到运行结果中偶尔会出现多个窗口卖同一张票的情况:
五、synchronized(同步锁)解决并发安全性问题
(一)语法:
synchronized(锁对象){
//操作共享资源的代码
}
(二)同步块加在什么地方:
- 代码被多个线程访问
- 代码中有共享的资源
- 共享数据被多条语句操作
(三)上述抢票问题的解决示例:
public class Test { public static void main(String[] args) { // 创建线程 SaleTicket st1 = new SaleTicket(); SaleTicket st2 = new SaleTicket(); SaleTicket st3 = new SaleTicket(); SaleTicket st4 = new SaleTicket(); // 线程指定名称 st1.setName("窗口1"); st2.setName("窗口2"); st3.setName("窗口3"); st4.setName("窗口4"); // 线程启动 st1.start(); st2.start(); st3.start(); st4.start(); }}/** * 卖票的窗口 * * */class SaleTicket extends Thread { // 总票数 private static int tickets = 100; //同步锁对象 private static Object obj=new Object(); @Override public void run() { while (true) { synchronized(obj){ if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } System.out.println(this.getName() + "正在卖" + tickets-- + "张票"); } else { System.out.println("票已经售完"); break; } } } }}
六、synchronized(同步锁)对象剖析
- synchronized同步块的锁对象可以是任意类对象(前提是:线程的实现方式是使用继承于Thread的方式),这个对象必须是线程类共享的(静态的)
- synchronized是可以加在方法上的,如果是静态方法,则synchronized的锁对象就是类的类对象,如果不是静态方法,则synchronized的锁对象就是当前对象(this)
七、线程的休眠/阻塞(使用sleep)
public class Test1 { public static void main(String[] args) { SleepDemo sd=new SleepDemo(); Thread t=new Thread(sd); t.start(); }}class SleepDemo implements Runnable{ @Override public void run() { while(true){ try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } System.out.println(new Date()); } }}
注意:
- sleep的异常不能够被抛出,必须进行catch处理
- 如果sleep()方法的调用在同步块中,线程的休眠不会让出锁对象(同步依然有效)
八、线程间的通信
生成者和消费者模式
示例:
public class Test3 { public static void main(String[] args) { Fruit f = new Fruit(); f.setName("苹果"); f.setExsit(false); ProductFruit pf = new ProductFruit(f); BuyFruit bf = new BuyFruit(f); Thread t1 = new Thread(pf); Thread t2 = new Thread(bf); t1.start(); t2.start(); }}/** * 水果 * */class Fruit { private String name; private boolean isExsit; public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isExsit() { return isExsit; } public void setExsit(boolean isExsit) { this.isExsit = isExsit; }}/** * 生产者线程 * */class ProductFruit implements Runnable { private Fruit fruit; public ProductFruit(Fruit fruit) { super(); this.fruit = fruit; } @Override public void run() { while (true) { // 有共享的数据,多个线程操作共享的数据,必须使用锁 synchronized (fruit) { // 如果水果已经存在那么生产者就不生产,等待着消费者买走水果再生产 if (fruit.isExsit()) { try { // 当前生产水果的线程被挂起变成阻塞状态 fruit.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } try { Thread.sleep(100); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } System.out.println(fruit.getName() + "水果被生产出来"); // 把水果的状态变成存在 fruit.setExsit(true); // 唤醒等待买水果的线程 fruit.notify(); } } }}/** * 消费者线程 * */class BuyFruit implements Runnable { private Fruit fruit; public BuyFruit(Fruit fruit) { super(); this.fruit = fruit; } @Override public void run() { while (true) { // 有共享的数据,多个线程操作共享的数据,必须使用锁 synchronized (fruit) { if (!fruit.isExsit()) { try { fruit.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } try { Thread.sleep(100); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } System.out.println(fruit.getName() + "水果被买走"); // 把水果的状态变成不存在 fruit.setExsit(false); // 唤醒等待生产水果的线程 fruit.notify(); } } }}
阅读全文
0 0
- Java学习笔记—浅谈多线程
- Java多线程学习笔记
- Java多线程学习笔记
- Java学习笔记---多线程
- java多线程学习笔记
- Java多线程学习笔记
- Java多线程学习笔记
- JAVA多线程学习笔记
- Java 多线程学习笔记
- java多线程学习笔记
- Java多线程学习笔记
- [学习笔记]Java多线程
- java多线程学习笔记
- Java多线程学习笔记
- Java学习笔记-多线程
- Java多线程学习笔记
- Java多线程学习笔记
- Java多线程学习笔记
- 小米机顶盒安装第三方软件流程
- 序列化
- Pygame开发打飞机游戏
- centos rar解压
- 表达式树---中缀表达式转后缀表达式
- Java学习笔记—浅谈多线程
- 拦截器的配置文件
- Java 数组
- 一篇博客让你了解Material Design的使用
- session 和 cookie
- activiti工作流
- python-6
- AIM Tech Round 4 (Div. 2) B. Rectangles
- 网站地址中的www有还是没有