多线程概述
java
package cn.itcast_01; /* * 进程: * 正在运行的程序,是系统进行资源分配和调用的独立单位。 * 每个进程都有它本身的内存空间和系统资源。 * 线程: * 是进程中的单个顺序控制流,是一条执行路径 * 一个进程若是只有一条执行路径,则称为单线程程序。 * 一个进程若是有多条执行路径,则称为多线程程序。 * * 举例: * 扫雷程序,迅雷下载 * * 你们注意两个词汇的区别:并行和并发。 * 前者是逻辑上同时发生,指在某一个时间内同时运行多个程序。 * 后者是物理上同时发生,指在某一个时间点同时运行多个程序。 * * Java程序的运行原理: * 由java命令启动JVM,JVM启动就至关于启动了一个进程。 * 接着有该进程建立了一个主线程去调用main方法。 * * 思考题: * jvm虚拟机的启动是单线程的仍是多线程的? * 多线程的。 * 缘由是垃圾回收线程也要先启动,不然很容易会出现内存溢出。 * 如今的垃圾回收线程加上前面的主线程,最低启动了两个线程,因此,jvm的启动实际上是多线程的。 */ public class MyThreadDemo { public static void main(String[] args) { System.out.println("hello"); new Object(); new Object(); new Object(); new Object(); //... System.out.println("world"); } }
获取和设置线程对象名称
安全
package cn.itcast_03; public class MyThread extends Thread { public MyThread() { } public MyThread(String name){ super(name); } @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println(getName() + ":" + x); } } }
package cn.itcast_03; /* * 如何获取线程对象的名称呢? * public final String getName():获取线程的名称。 * 如何设置线程对象的名称呢? * public final void setName(String name):设置线程的名称 * * 针对不是Thread类的子类中如何获取线程对象名称呢? * public static Thread currentThread():返回当前正在执行的线程对象 * Thread.currentThread().getName() */ public class MyThreadDemo { public static void main(String[] args) { // 建立线程对象 //无参构造+setXxx() // MyThread my1 = new MyThread(); // MyThread my2 = new MyThread(); // //调用方法设置名称 // my1.setName("林青霞"); // my2.setName("刘意"); // my1.start(); // my2.start(); //带参构造方法给线程起名字 // MyThread my1 = new MyThread("林青霞"); // MyThread my2 = new MyThread("刘意"); // my1.start(); // my2.start(); //我要获取main方法所在的线程对象的名称,该怎么办呢? //遇到这种状况,Thread类提供了一个很好玩的方法: //public static Thread currentThread():返回当前正在执行的线程对象 System.out.println(Thread.currentThread().getName()); } } /* 名称为何是:Thread-? 编号 class Thread { private char name[]; public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } private void init(ThreadGroup g, Runnable target, String name, long stackSize) { init(g, target, name, stackSize, null); } private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) { //大部分代码被省略了 this.name = name.toCharArray(); } public final void setName(String name) { this.name = name.toCharArray(); } private static int threadInitNumber; //0,1,2 private static synchronized int nextThreadNum() { return threadInitNumber++; //return 0,1 } public final String getName() { return String.valueOf(name); } } class MyThread extends Thread { public MyThread() { super(); } } */
线程调度及获取和设置线程优先级
多线程
package cn.itcast_04; /* * 咱们的线程没有设置优先级,确定有默认优先级。 * 那么,默认优先级是多少呢? * 如何获取线程对象的优先级? * public final int getPriority():返回线程对象的优先级 * 如何设置线程对象的优先级呢? * public final void setPriority(int newPriority):更改线程的优先级。 * * 注意: * 线程默认优先级是5。 * 线程优先级的范围是:1-10。 * 线程优先级高仅仅表示线程获取的 CPU时间片的概率高,可是要在次数比较多,或者屡次运行的时候才能看到比较好的效果。 * * IllegalArgumentException:非法参数异常。 * 抛出的异常代表向方法传递了一个不合法或不正确的参数。 * */ public class ThreadPriorityDemo { public static void main(String[] args) { ThreadPriority tp1 = new ThreadPriority(); ThreadPriority tp2 = new ThreadPriority(); ThreadPriority tp3 = new ThreadPriority(); tp1.setName("东方不败"); tp2.setName("岳不群"); tp3.setName("林平之"); // 获取默认优先级 // System.out.println(tp1.getPriority()); // System.out.println(tp2.getPriority()); // System.out.println(tp3.getPriority()); // 设置线程优先级 // tp1.setPriority(100000); //设置正确的线程优先级 tp1.setPriority(10); tp2.setPriority(1); tp1.start(); tp2.start(); tp3.start(); } }
线程控制之休眠线程
并发
package cn.itcast_04; import java.util.Date; public class ThreadSleep extends Thread { @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println(getName() + ":" + x + ",日期:" + new Date()); // 睡眠 // 困了,我稍微休息1秒钟 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
线程控制之加入线程
jvm
package cn.itcast_04; /* * public final void join():等待该线程终止。 */ public class ThreadJoinDemo { public static void main(String[] args) { ThreadJoin tj1 = new ThreadJoin(); ThreadJoin tj2 = new ThreadJoin(); ThreadJoin tj3 = new ThreadJoin(); tj1.setName("李渊"); tj2.setName("李世民"); tj3.setName("李元霸"); tj1.start(); try { tj1.join(); } catch (InterruptedException e) { e.printStackTrace(); } tj2.start(); tj3.start(); } }
线程控制之礼让线程
ide
package cn.itcast_04; public class ThreadYield extends Thread { @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println(getName() + ":" + x); Thread.yield(); } } }
package cn.itcast_04; /* * public static void yield():暂停当前正在执行的线程对象,并执行其余线程。 * 让多个线程的执行更和谐,可是不能靠它保证一人一次。 */ public class ThreadYieldDemo { public static void main(String[] args) { ThreadYield ty1 = new ThreadYield(); ThreadYield ty2 = new ThreadYield(); ty1.setName("林青霞"); ty2.setName("刘意"); ty1.start(); ty2.start(); } }
线程控制之守护线程
函数
package cn.itcast_04; public class ThreadDaemon extends Thread { @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println(getName() + ":" + x); } } }
package cn.itcast_04; /* * public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程。 * 当正在运行的线程都是守护线程时,Java 虚拟机退出。 该方法必须在启动线程前调用。 * * 游戏:坦克大战。 */ public class ThreadDaemonDemo { public static void main(String[] args) { ThreadDaemon td1 = new ThreadDaemon(); ThreadDaemon td2 = new ThreadDaemon(); td1.setName("关羽"); td2.setName("张飞"); // 设置收获线程 td1.setDaemon(true); td2.setDaemon(true); td1.start(); td2.start(); Thread.currentThread().setName("刘备"); for (int x = 0; x < 5; x++) { System.out.println(Thread.currentThread().getName() + ":" + x); } } }
线程控制之中断线程
this
package cn.itcast_04; import java.util.Date; public class ThreadStop extends Thread { @Override public void run() { System.out.println("开始执行:" + new Date()); // 我要休息10秒钟,亲,不要打扰我哦 try { Thread.sleep(10000); } catch (InterruptedException e) { // e.printStackTrace(); System.out.println("线程被终止了"); } System.out.println("结束执行:" + new Date()); } }
package cn.itcast_04; /* * public final void stop():让线程中止,过期了,可是还可使用。 * public void interrupt():中断线程。 把线程的状态终止,并抛出一个InterruptedException。 */ public class ThreadStopDemo { public static void main(String[] args) { ThreadStop ts = new ThreadStop(); ts.start(); // 你超过三秒不醒过来,我就干死你 //主要还看主函数的,主函数要求他睡了3秒,3秒后,将其终止了, //该线程3秒后已经死掉了,因此10秒后是不会醒来的 try { Thread.sleep(3000); // ts.stop(); ts.interrupt(); } catch (InterruptedException e) { e.printStackTrace(); } } }
多线程实现方式二:Ruannable
线程
package cn.itcast_05; public class MyRunnable implements Runnable { @Override public void run() { for (int x = 0; x < 100; x++) { // 因为实现接口的方式就不能直接使用Thread类的方法了,可是能够间接的使用 System.out.println(Thread.currentThread().getName() + ":" + x); } } }
package cn.itcast_05; /* * 方式2:实现Runnable接口 * 步骤: * A:自定义类MyRunnable实现Runnable接口 * B:重写run()方法 * C:建立MyRunnable类的对象 * D:建立Thread类的对象,并把C步骤的对象做为构造参数传递 */ public class MyRunnableDemo { public static void main(String[] args) { // 建立MyRunnable类的对象 MyRunnable my = new MyRunnable(); // 建立Thread类的对象,并把C步骤的对象做为构造参数传递 // Thread(Runnable target) // Thread t1 = new Thread(my); // Thread t2 = new Thread(my); // t1.setName("林青霞"); // t2.setName("刘意"); // Thread(Runnable target, String name) Thread t1 = new Thread(my, "林青霞"); Thread t2 = new Thread(my, "刘意"); t1.start(); t2.start(); } }
继承Thread类的方式卖电影票案例
设计
package cn.itcast_06; public class SellTicket extends Thread { // 定义100张票 // private int tickets = 100; // 为了让多个线程对象共享这100张票,咱们其实应该用静态修饰 private static int tickets = 100; @Override public void run() { // 定义100张票 // 每一个线程进来都会走这里,这样的话,每一个线程对象至关于买的是本身的那100张票,这不合理,因此应该定义到外面 // int tickets = 100; // 是为了模拟一直有票 while (true) { if (tickets > 0) { System.out.println(getName() + "正在出售第" + (tickets--) + "张票"); } } } }
package cn.itcast_06; /* * 某电影院目前正在上映贺岁大片(红高粱,少林寺传奇藏经阁),共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。 * 继承Thread类来实现。 */ public class SellTicketDemo { public static void main(String[] args) { // 建立三个线程对象 SellTicket st1 = new SellTicket(); SellTicket st2 = new SellTicket(); SellTicket st3 = new SellTicket(); // 给线程对象起名字 st1.setName("窗口1"); st2.setName("窗口2"); st3.setName("窗口3"); // 启动线程 st1.start(); st2.start(); st3.start(); } }
实现Runnable接口的方式卖电影票案例
package cn.itcast_07; public class SellTicket implements Runnable { // 定义100张票 private int tickets = 100; @Override public void run() { while (true) { if (tickets > 0) { System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票"); } } } }
package cn.itcast_07; /* * 实现Runnable接口的方式实现 */ public class SellTicketDemo { public static void main(String[] args) { // 建立资源对象 SellTicket st = new SellTicket(); // 建立三个线程对象 Thread t1 = new Thread(st, "窗口1"); Thread t2 = new Thread(st, "窗口2"); Thread t3 = new Thread(st, "窗口3"); // 启动线程 t1.start(); t2.start(); t3.start(); } }
卖电影票出现了同票和负数票的缘由分析
package cn.itcast_08; public class SellTicket implements Runnable { // 定义100张票 private int tickets = 100; // @Override // public void run() { // while (true) { // // t1,t2,t3三个线程 // // 这一次的tickets = 100; // if (tickets > 0) { // // 为了模拟更真实的场景,咱们稍做休息 // try { // Thread.sleep(100); // t1就稍做休息,t2就稍做休息 // } catch (InterruptedException e) { // e.printStackTrace(); // } // // System.out.println(Thread.currentThread().getName() + "正在出售第" // + (tickets--) + "张票"); // // 理想状态: // // 窗口1正在出售第100张票 // // 窗口2正在出售第99张票 // // 可是呢? // // CPU的每一次执行必须是一个原子性(最简单基本的)的操做。 // // 先记录之前的值 // // 接着把ticket-- // // 而后输出之前的值(t2来了) // // ticket的值就变成了99 // // 窗口1正在出售第100张票 // // 窗口2正在出售第100张票 // // } // } // } @Override public void run() { while (true) { // t1,t2,t3三个线程 // 这一次的tickets = 1; if (tickets > 0) { // 为了模拟更真实的场景,咱们稍做休息 try { Thread.sleep(100); //t1进来了并休息,t2进来了并休息,t3进来了并休息, } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票"); //窗口1正在出售第1张票,tickets=0 //窗口2正在出售第0张票,tickets=-1 //窗口3正在出售第-1张票,tickets=-2 } } } }
package cn.itcast_08; /* * 实现Runnable接口的方式实现 * * 经过加入延迟后,就产生了连个问题: * A:相同的票卖了屡次 * CPU的一次操做必须是原子性的 * B:出现了负数票 * 随机性和延迟致使的 */ public class SellTicketDemo { public static void main(String[] args) { // 建立资源对象 SellTicket st = new SellTicket(); // 建立三个线程对象 Thread t1 = new Thread(st, "窗口1"); Thread t2 = new Thread(st, "窗口2"); Thread t3 = new Thread(st, "窗口3"); // 启动线程 t1.start(); t2.start(); t3.start(); } }
线程安全问题的产生缘由分析
package cn.itcast_09; public class SellTicket implements Runnable { // 定义100张票 private int tickets = 100; //建立锁对象 private Object obj = new Object(); // @Override // public void run() { // while (true) { // synchronized(new Object()){ // if (tickets > 0) { // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName() + "正在出售第" // + (tickets--) + "张票"); // } // } // } // } @Override public void run() { while (true) { synchronized (obj) { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票"); } } } } }
package cn.itcast_09; /* * 如何解决线程安全问题呢? * * 要想解决问题,就要知道哪些缘由会致使出问题:(并且这些缘由也是之后咱们判断一个程序是否会有线程安全问题的标准) * A:是不是多线程环境 * B:是否有共享数据 * C:是否有多条语句操做共享数据 * * 咱们来回想一下咱们的程序有没有上面的问题呢? * A:是不是多线程环境 是 * B:是否有共享数据 是 * C:是否有多条语句操做共享数据 是 * * 因而可知咱们的程序出现问题是正常的,由于它知足出问题的条件。 * 接下来才是咱们要想一想如何解决问题呢? * A和B的问题咱们改变不了,咱们只能想办法去把C改变一下。 * 思想: * 把多条语句操做共享数据的代码给包成一个总体,让某个线程在执行的时候,别人不能来执行。 * 问题是咱们不知道怎么包啊?其实我也不知道,可是Java给咱们提供了:同步机制。 * * 同步代码块: * synchronized(对象){ * 须要同步的代码; * } * * A:对象是什么呢? * 咱们能够随便建立一个对象试试。 * B:须要同步的代码是哪些呢? * 把多条语句操做共享数据的代码的部分给包起来 * * 注意: * 同步能够解决安全问题的根本缘由就在那个对象上。该对象如同锁的功能。 * 多个线程必须是同一把锁。 */ public class SellTicketDemo { public static void main(String[] args) { // 建立资源对象 SellTicket st = new SellTicket(); // 建立三个线程对象 Thread t1 = new Thread(st, "窗口1"); Thread t2 = new Thread(st, "窗口2"); Thread t3 = new Thread(st, "窗口3"); // 启动线程 t1.start(); t2.start(); t3.start(); } }
同步代码块的方式解决线程安全问题
package cn.itcast_10; public class SellTicket implements Runnable { // 定义100张票 private int tickets = 100; // 定义同一把锁 private Object obj = new Object(); @Override public void run() { while (true) { // t1,t2,t3都能走到这里 // 假设t1抢到CPU的执行权,t1就要进来 // 假设t2抢到CPU的执行权,t2就要进来,发现门是关着的,进不去。因此就等着。 // 门(开,关) synchronized (obj) { // 发现这里的代码未来是会被锁上的,因此t1进来后,就锁了。(关) if (tickets > 0) { try { Thread.sleep(100); // t1就睡眠了 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票 "); //窗口1正在出售第100张票 } } //t1就出来可,而后就开门。(开) } } }
package cn.itcast_10; /* * 举例: * 火车上厕所。 * * 同步的特色: * 前提: * 多个线程 * 解决问题的时候要注意: * 多个线程使用的是同一个锁对象 * 同步的好处 * 同步的出现解决了多线程的安全问题。 * 同步的弊端 * 当线程至关多时,由于每一个线程都会去判断同步上的锁,这是很耗费资源的,无形中会下降程序的运行效率。 */ public class SellTicketDemo { public static void main(String[] args) { // 建立资源对象 SellTicket st = new SellTicket(); // 建立三个线程对象 Thread t1 = new Thread(st, "窗口1"); Thread t2 = new Thread(st, "窗口2"); Thread t3 = new Thread(st, "窗口3"); // 启动线程 t1.start(); t2.start(); t3.start(); } }
同步代码块的锁及同步方法应用和锁的问题
package cn.itcast_11; public class SellTicket implements Runnable { // 定义100张票 private static int tickets = 100; // 定义同一把锁 private Object obj = new Object(); private Demo d = new Demo(); private int x = 0; //同步代码块用obj作锁 // @Override // public void run() { // while (true) { // synchronized (obj) { // if (tickets > 0) { // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName() // + "正在出售第" + (tickets--) + "张票 "); // } // } // } // } //同步代码块用任意对象作锁 // @Override // public void run() { // while (true) { // synchronized (d) { // if (tickets > 0) { // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName() // + "正在出售第" + (tickets--) + "张票 "); // } // } // } // } @Override public void run() { while (true) { if(x%2==0){ synchronized (SellTicket.class) { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票 "); } } }else { // synchronized (d) { // if (tickets > 0) { // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName() // + "正在出售第" + (tickets--) + "张票 "); // } // } sellTicket(); } x++; } } // private void sellTicket() { // synchronized (d) { // if (tickets > 0) { // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName() // + "正在出售第" + (tickets--) + "张票 "); // } // } // } //若是一个方法一进去就看到了代码被同步了,那么我就再想能不能把这个同步加在方法上呢? // private synchronized void sellTicket() { // if (tickets > 0) { // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName() // + "正在出售第" + (tickets--) + "张票 "); // } // } private static synchronized void sellTicket() { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票 "); } } } class Demo { }
package cn.itcast_11; /* * A:同步代码块的锁对象是谁呢? * 任意对象。 * * B:同步方法的格式及锁对象问题? * 把同步关键字加在方法上。 * * 同步方法是谁呢? * this * * C:静态方法及锁对象问题? * 静态方法的锁对象是谁呢? * 类的字节码文件对象。(反射会讲) */ public class SellTicketDemo { public static void main(String[] args) { // 建立资源对象 SellTicket st = new SellTicket(); // 建立三个线程对象 Thread t1 = new Thread(st, "窗口1"); Thread t2 = new Thread(st, "窗口2"); Thread t3 = new Thread(st, "窗口3"); // 启动线程 t1.start(); t2.start(); t3.start(); } }
之前的线程安全的类回顾
package cn.itcast_12; import java.util.ArrayList; import java.util.Collections; import java.util.Hashtable; import java.util.List; import java.util.Vector; public class ThreadDemo { public static void main(String[] args) { // 线程安全的类 StringBuffer sb = new StringBuffer(); Vector<String> v = new Vector<String>(); Hashtable<String, String> h = new Hashtable<String, String>(); // Vector是线程安全的时候才去考虑使用的,可是我还说过即便要安全,我也不用你 // 那么到底用谁呢? // public static <T> List<T> synchronizedList(List<T> list) List<String> list1 = new ArrayList<String>();// 线程不安全 List<String> list2 = Collections .synchronizedList(new ArrayList<String>()); // 线程安全 } }