[2017.12.02-03]多线程
来源:互联网 发布:c语言编程步骤 编辑:程序博客网 时间:2024/06/11 07:01
多线程
1.多线程:如果一个程序的执行路径有多条.
单线程:程序的执行路径只有一条
2.面试题:Jvm,java虚拟机是多线程程序吗?
是多线程程序,由于java虚拟机中自带一个垃圾回收器,来确保程序不会轻易的造成内存溢出!
至少开启两条子线程:
当前程序在执行代码的时候,会开启main:主线程
垃圾回收器会开启一个垃圾回收线程,来确保程序不会内存异常,将不用的变量或者没有更多引用的对象来回收掉!
3.演示:
public class ThreadDemo { public static void main(String[] args) { System.out.println(new Object()) ; System.out.println(new Object()) ; System.out.println(new Object()) ; System.out.println(new Object()) ; //....中间创建了很多对象 System.out.println(new Object()) ; }}
4.重点:如何实现多线程程序?
要实现多线程程序,必须创建一个进程,
创建进程需要调用系统资源进行创建,但是Java语言是不能直接调用系统资源
C/C++语言是可以创建系统资源,然后使用Java语言掉C/C++已经封装好的东西,
Java—->类:Thread类
5.并发和并行
并发:指的是同一个时间点
并行:指的的是一个时间段
6.多线程程序实现方式1:
1)自定一个类:MyThread 继承自Thread类
2)在MyThread类中重写Thread类中的run() :为什么重写run()
3)在主线程中,创建该类的实例对象,启动线程
7.演示:
public class ThreadDemo { public static void main(String[] args) { //创建该线程的实例对象// MyThread my = new MyThread() ;// // //启动线程// /**// * 启动线程不是调用run()方法,// * strat()是线程开始执行的方法 //run()方法调用相当于调用了一个普通方法,并不会出现线程随机性;而start()方法调用, //其实是通过Jvm调用线程中的run()来进行多个线程抢占CPU执行权// */// my.start() ;// //0java.lang.IllegalThreadStateException// /**// * 第二次启动线程,会出现:非法线程状态异常// * 当前my线程已经启动了,不能重新启动// */// my.start() ;// my.run() ;// my.run() ; MyThread my1 = new MyThread() ; MyThread my2 = new MyThread() ; //分别启动线程 my1.start() ; my2.start() ; }}
//自定义一个线程类:MyThread/** * 为什么重写run(),run方法里面执行的耗时的操作. * @author Apple */public class MyThread extends Thread { /* * 重写Thread类中的run()方法 * */ @Override public void run() {// System.out.println("helloworld"); //耗时的操作,线程睡眠,线程等待,循环语句 for(int x = 0 ; x <100 ; x ++){ System.out.println(x); } }}
8.如何获取线程的名称?
public final String getName()返回该线程的名称。
设置线程名称
public final void setName(String name)改变线程名称,使之与参数 name 相同。
9.演示:
public class ThreadDemo { public static void main(String[] args) { //创建线程类的实例 //无参构造的形式// MyThread my1 = new MyThread() ;// MyThread my2 = new MyThread() ; //有参构造形式设置线程名称// MyThread my1 = new MyThread("高圆圆") ;// MyThread my2 = new MyThread("张杨") ; /* * 设置线程名称方式:无参+setName(String name)方法 * */ MyThread my1 = new MyThread() ; MyThread my2 = new MyThread() ; // public final void setName(String name)改变线程名称,使之与参数 name 相同。 my1.setName("高圆圆") ; my2.setName("张杨") ; //public static Thread currentThread():表示正在运行的线程 //默认所有的子线程的都在主线程中 System.out.println(Thread.currentThread().getName());//main //启动线程 my1.start() ; my2.start() ; }}/* * * public class Thread implements Runnable{ * private char name[]; * * public Thread() { * 初始化 init(null, null, "Thread-" + nextThreadNum(), 0); //"Thread-" + nextThreadNum() } 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) { if (name == null) { throw new NullPointerException("name cannot be null"); } this.name = name.toCharArray();//字符数组 //此处省略了代码 } rivate static int threadInitNumber; private static synchronized(同步机制) int nextThreadNum() { return threadInitNumber++;//0,1,2.. } public final String getName() { return String.valueOf(name);//将当前name字符数组转换成字符串了 } } * * */
public class MyThread extends Thread { //无参构造 public MyThread(){ } //有参构造 public MyThread(String name){ super(name) ; } @Override //my1和my2子线程都会执行这段代码,两个子线程在互相抢占CPU的执行权 public void run() { for(int x = 0 ; x <200; x ++){// System.out.println(x); System.out.println(getName() +":"+x); } }}
10.public final int getPriority()返回线程的优先级。
线程的优先级有哪些?
默认优先级:5
java.lang.Thread
public static final int MAX_PRIORITY 10 :最大优先级 优先级大的抢占到CPU的执行权大,并不代表就一定能抢到,因为线程的执行具有随机性!
public static final int MIN_PRIORITY 1 :最小优先级
public static final int NORM_PRIORITY 5 :默认优先级
11.演示:
//测试类public class ThreadProrityDemo { public static void main(String[] args) { //创建该线程的实例 //并且设置线程名称 MyPriority mp1 = new MyPriority() ; MyPriority mp2 = new MyPriority() ; MyPriority mp3 = new MyPriority() ; //设置线程名称:无参构造+setName()方法// //获取线程的优先级// System.out.println(mp1.getPriority());//5// System.out.println(mp2.getPriority());//5// System.out.println(mp3.getPriority());//5 mp1.setName("张飞") ; mp2.setName("刘备") ; mp3.setName("关羽") ; //给线程设置优先级 mp1.setPriority(10) ; mp2.setPriority(1) ; //启动线程 mp1.start() ; mp2.start() ; mp3.start() ; }}
//自定义的线程类public class MyPriority extends Thread { @Override public void run() { for(int x = 0 ; x <100 ; x ++){ System.out.println(getName()+":"+x); } }}
12.public final void join()
throws InterruptedException等待该线程终止。
13.演示:
public class JoinDemo { public static void main(String[] args) { /* * 创建该线程的实例对象 * */ JoinThread jt1 = new JoinThread() ; JoinThread jt2 = new JoinThread() ; JoinThread jt3 = new JoinThread() ; //设置线程名称 jt1.setName("李世民") ; jt2.setName("李元霸") ; jt3.setName("李渊") ; //启动线程 jt1.start() ; //设置线程等待该线程终止该方法必须要启动线程 try { jt1.join() ; } catch (InterruptedException e) { //InterruptedException:中断异常 e.printStackTrace(); } jt2.start() ; jt3.start() ; }}
14.public static void yield()暂停当前正在执行的线程对象,并执行其他线程。
暂停当前线程执行其他线程,并不保证另一个线程就一定能抢占到CPU的执行权
15.演示:
public class YieldThreadDemo { public static void main(String[] args) { //创建线程类对象 YieldThread yt1 = new YieldThread() ; YieldThread yt2 = new YieldThread() ; //设置线程名称 yt1.setName("高圆圆") ; yt2.setName("赵又廷") ; //启动线程 yt1.start() ; yt2.start() ; }}
/* *自定义线程类 * */public class YieldThread extends Thread { @Override public void run() { //yt1,yt2 for(int x =0 ; x <100 ; x ++){ System.out.println(getName()+":"+x); Thread.yield() ; } }}
16.线程停止:
public final void stop():强迫线程停止执行
public void interrupt()中断线程。 表示中断线程一种状态
join()
yield()
stop()
setDeamon(boolean on):(用的多)
sleep():线程睡眠 (用的多)
wait():线程等待
17.演示:
public class ThreadStopDemo { public static void main(String[] args) { //创建线程类对象 ThreadStop ts = new ThreadStop() ; ts.start() ; //如果3秒中不醒来,干掉它 try { Thread.sleep(3000) ;// ts.stop() ;//该方法表示过时,但是可以用它 //强迫线程停止执行 ts.interrupt() ;//中断一种状态 } catch (InterruptedException e) { e.printStackTrace(); } }}
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()); }}
18.线程睡眠:
public static void sleep(long millis)
throws InterruptedException在指定的毫秒数内让当前正在执行的线程休眠(暂停执行
19.演示:
public class ThreadSleepDemo { public static void main(String[] args) { //创建线程类对象 ThreadSellp ts1 = new ThreadSellp() ; ThreadSellp ts2 = new ThreadSellp() ; ThreadSellp ts3 = new ThreadSellp() ; //设置线程名称 ts1.setName("线程1") ; ts2.setName("线程2") ; ts3.setName("线程3") ; //启动线程 ts1.start() ; ts2.start() ; ts3.start() ; }}
import java.util.Date;public class ThreadSellp extends Thread { @Override public void run() { //ts1,ts2,ts3 for(int x = 0 ; x <100 ; x ++){ System.out.println(getName()+":"+x+",日期:"+new Date()); //睡眠1秒中:1000毫秒 try { Thread.sleep(1000) ;//此方法本身就抛出一个异常! } catch (InterruptedException e) { e.printStackTrace(); } } }}
20.public final void setDaemon(boolean on) on指定true,就是设置守护线程…
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
该方法必须在启动线程前调用。
jvm自动退出,对于主线程的数据如果直接输出完毕,对于两个守护线程来说不会立即消失,Jvm等会就自动退出.
比如:坦克大战
21.演示:
public class ThreadDemaonDemo { public static void main(String[] args) { //创建线程类对象 ThreadDeamon td1 = new ThreadDeamon() ; ThreadDeamon td2 = new ThreadDeamon() ; //设置线程名称 td1.setName("张飞") ; td2.setName("关羽") ; //setDaemon(boolean on) 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); //设置主线程 } }}
public class ThreadDeamon extends Thread { @Override public void run() { for(int x = 0 ; x <100 ; x ++){ System.out.println(getName()+":"+x); } }}
22.多线程实现的第二种方式:(实际开发中第二种比第一种应用更广泛)
开发步骤:
1)自定义一个类MyRunnable,该类实现Runnable接口
2)实现该接口中的run()方法
3)在主线程中创建该类的实例对象,
4)创建Thread类对象,将3)创建的这个对象作为参数进行传递
5)分别启动线程
23.面试题:多线程的实现方式有几种,分别是什么?答三个方式,分别阐述
第三种:和线程池有关系(并且和Callable接口有关系)
24.演示:
public class ThreadDemo { public static void main(String[] args) { //1)创建MyRunnable实例对象 MyRunnable my = new MyRunnable() ; //2)创建线程类对象 //Thread线程类中的构造方法 //public Thread(Runnable target)// Thread t1 = new Thread(my) ;// t1.setName("线程1") ; //public Thread(Runnable target,String name) Thread t1 = new Thread(my, "线程1") ; Thread t2 = new Thread(my, "线程2") ; //启动线程 t1.start() ; t2.start() ; }}
public class MyRunnable implements Runnable { @Override //run()中的方法也是需要一些耗时的操作 public void run() { for(int x = 0 ; x < 100 ; x ++){ //在控制台,打印每一个子线程的线程名称// System.out.println(getName()+":"+x); //getName()是Thread类中的方法 //间接的使用Thread类的静态功能得到线程名称 System.out.println(Thread.currentThread().getName()+":"+x); } }}
25.演示:
/* * 某电影院目前正在上映贺岁大片(红高粱,少林寺传奇藏经阁),共有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() ; }}
public class SellTicket extends Thread { //定义100张票// private int tickets = 100 ; //这100张票应该被三个线程共用,所以用static修饰 private static int tickets = 100 ; @Override public void run() { //st1,st2,st3都要执行run()方法中代码 //有100张票// int tickets = 100 ;(将这个代码放到run()方法的外面) //模拟该电影院售票:电影院一直有票 while(true){ if(tickets>0){ System.out.println(getName() + "正在出售第" + (tickets--) + "张票"); } } }}
26.演示:
//接口:自定义类实现Runnable接口(研究主要接口的形式)/* * 为了模拟电影院卖票更真实的场景,每一个窗口卖票应该延迟操作 * 在接口自实现类中,在run()方法中让每一个线程执行睡眠0.1秒 * *加入延迟操作: * 1)一张票可能被卖多次 * 100张票会出现多次 * CPU的执行具有原子性操作 * 2)可能出现负票 * 1,0,-1 * 延迟操作和线程随机性导致 * * *这两种情况都属于线程安全问题,现在写的这个代码是一种有问题的代码? * 如何解决多线程的安全问题呢? * 检验多线程安全问题的标准(以后在判断一个多线程序是否有安全问题的标准) * 1)当前是否是一个多线程环境 * 2)多线程环境中是否有共享数据 * 3)是否有多条语句对共享数据进行操作 * *就目前这个电影院卖票这个案例, * 1)是多线程环境,因为要实现3个窗口同时卖票 * 2)有共享数据,比如:tickets * 3)有多条语句对当前这个共享数据进行操作 * *优化改进: * 1)多线程环境和共享数据改变不了,突破口就是3)条件:将多条语句对共享数据的操作进行更改 * * 将多条语句对共享数据进行操作的代码用代码块包起来 * Java的同步机制: * 使用同步代码块:synchronized(同步锁对象){ * 多条语句对共享数据的操作; * } * *同步锁对象:应该每一个线程都要使用这个锁对象(同步锁):理解为门的开和关 *使用同步代码块可以解决线程安全问题 * *同步锁对象可以是什么样的对象 * 可以是Object类型,任意的Java类 * */public class SellTicketDemo { public static void main(String[] args) { //创建SellTicket对象 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() ; }}
public class SellTicket implements Runnable { //100张票被共用,不让外界修改数据 private static int tickets = 100 ; //同步锁对象 private Object obj = new Object() ; @Override public void run() { // 模拟电影院一直有票 //t1,t2,t3子线程要执行循环语句中的内容 while (true) { //t1线程先抢占CPU的执行权 //理解为门的开和关 //t1,t2,t3继续抢占CPU的执行权 //t2抢占到了,门一关,t1,t3进不来了 synchronized(obj){//t1进来,门关了,t2和t3线程都不会进来 if (tickets > 0) { try { Thread.sleep(100) ; } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票"); //窗口1正在出售第100张 //窗口正在出售第99张 } }//代码执行完毕,t1线程出来,意味着门开了... ////代码执行完毕,t1线程出来,意味着门开了... } }}
27.同步锁定对象:
1)可以Object类型以及任意的Java类型对象
2)如果一个方法进来之后是一个同步代码块,那么同步代码块可以演变成一个同步方法
3)如果是一个静态的同步方法,锁对象是当前类名class属性:类名.class (反射机制:获取一些类的字节码文件对象Class类对象)
28.面试题:思考:
wait()线程等待,notify(),唤醒单个线程,notifyAll():唤醒所有线程这三个方法为什么不定义到Thread类中呢 而是定义在Object类中?
线程中会存在安全问题,并且解决线程安全问题使用的同步代码块或者同步方法来解决,同步代码块来解决线程安全问题,就存在
同步锁对象,谁能代表同步锁对象(Object以及任意的Java类),把它定义到Object类中;等待唤醒机制(生产者和消费者线程)
29.演示:
public class SellTicketDemo { public static void main(String[] args) { //创建SellTicket对象 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() ;}}
public class SellTicket implements Runnable { //100张票被共用,不让外界修改数据 private static int tickets = 100 ; //同步锁对象 private Object obj = new Object() ; //任意的类的对象 private Demo d = new Demo() ; private int x ; //设计模式:单例模式:懒汉式就和线程安全问题有关系 //Object类型对象// @Override// public void run() {// // // 模拟电影院一直有票// // //t1,t2,t3子线程要执行循环语句中的内容// while (true) {// // //t1线程先抢占CPU的执行权// //理解为门的开和关// //t1,t2,t3继续抢占CPU的执行权// //t2抢占到了,门一关,t1,t3进不来了// synchronized(obj){//t1进来,门关了,t2和t3线程都不会进来// // if (tickets > 0) {// try {// Thread.sleep(100) ;// } catch (InterruptedException e) {// e.printStackTrace();// }// System.out.println(Thread.currentThread().getName() + "正在出售第"// + (tickets--) + "张票");// //窗口1正在出售第100张// //窗口正在出售第99张// }// }//代码执行完毕,t1线程出来,意味着门开了... // ////代码执行完毕,t1线程出来,意味着门开了... // // // }// } //同步锁对象:任意的自定义类的对象// @Override// public void run() {// // // 模拟电影院一直有票// // //t1,t2,t3子线程要执行循环语句中的内容// while (true) {// // //t1线程先抢占CPU的执行权// //理解为门的开和关// //t1,t2,t3继续抢占CPU的执行权// //t2抢占到了,门一关,t1,t3进不来了// synchronized(d){//t1进来,门关了,t2和t3线程都不会进来// // if (tickets > 0) {// try {// Thread.sleep(100) ;// } catch (InterruptedException e) {// e.printStackTrace();// }// System.out.println(Thread.currentThread().getName() + "正在出售第"// + (tickets--) + "张票");// //窗口1正在出售第100张// //窗口正在出售第99张// }// }//代码执行完毕,t1线程出来,意味着门开了... // ////代码执行完毕,t1线程出来,意味着门开了... // // // }// } @Override public void run() { // 模拟电影院一直有票 //t1,t2,t3子线程要执行循环语句中的内容 while (true) { if(x%2==0){ synchronized(SellTicket.class){//静态同步方法:要通过反射获取Class类对象(当前类的字节码文件对象) if (tickets > 0) { try { Thread.sleep(100) ; } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票"); //窗口1正在出售第100张 //窗口正在出售第99张 } } }else{ 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--) + "张票");// // }// }// } //同步方法 (非静态的同步方法) //对于同步方法来说,它的同步锁对象是this// public synchronized void sellTicket(){// if (tickets > 0) {// try {// Thread.sleep(100);// } catch (InterruptedException e) {// e.printStackTrace();// }// System.out.println(Thread.currentThread().getName()// + "正在出售第" + (tickets--) + "张票");// // }// } //如果是静态同步方法,那么同步锁对象是谁呢? public 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{}
30.使用同步机制的这种方式解决线程安全问题,但是不知道具体的锁对象在哪里添加,并且锁对象在哪里释放锁对象,对于这种情况
Jdk5以后Java提供了一个更具体的锁对象:Lock
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作
Lock是一个接口,所以它在使用的是 ReentrantLock子实现类
public void lock()获取锁。
public void unlock()试图释放此锁
虽然Jdk5以后提供了具体的锁,但是面试的时候,肯定会问同步机制解决线程安全问题!
31.演示:
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() ; }}
import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class SellTicket implements Runnable { //定义一个100张票 private int tickets = 100 ; //定义锁对象// private Object obj = new Object() ; //Jdk5以后Java提供了更具的锁定操作:加锁和释放锁 //定义一个具体锁对象 private Lock lock = new ReentrantLock() ;//具体的lock锁 //捕获异常标准格式:try...catch...finally //变形格式:try...finally... /** * try{ * 可能出现问题的代码 * }catch(SocketException e){ * //不需要进行处理 * //空处理 * } */ @Override public void run() { //模拟电影院一直有票 while(true){ //同步机制// synchronized(obj){ try{ //获取锁 lock.lock() ; if(tickets>0){ //加入延迟操作 try { Thread.sleep(100) ; } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票"); } }finally{ //试图释放锁对象 lock.unlock() ; }// } } }}
32.使用同步机制可以解决多线程的安全问题,但是自身也会有弊端:
1)同步—->执行效率低(每一个线程在抢占到CPU的执行权,会去将(门)关闭,别的线程进不来)
2)容易出现死锁现象
死锁线程:两个或者两个以上的线程出现了互相等待的情况,就会出现死锁!
举例:
中国人和美国人吃饭
中国人:一双筷子
美国人:一把刀,一把叉
现在:
中国人:一根筷子和一把刀
美国人:一根筷子和一把叉
线程通信的问题:使用消费者和生产者模式
33.演示:
public class DieLockDemo { public static void main(String[] args) { //创建线程类对象 DieLock dl1 = new DieLock(true) ; DieLock dl2 = new DieLock(false); //启动线程 dl1.start() ; dl2.start() ; } //if objA else objB //else objB if objA /** * 理想状态 * if objA if objB else objB else objA */}
public class DieLock extends Thread { //定义一个成员变量 private boolean flag ; public DieLock(boolean flag){ this.flag = flag ; } //重写run()方法 @Override public void run() { //dl1,dl2线程 if(flag){ synchronized(MyLock.objA){ System.out.println("if objA"); synchronized (MyLock.objB) { System.out.println("if objB"); } }//代码执行完毕,objA锁相当于才能被释放掉 }else { //dl2 synchronized (MyLock.objB) { System.out.println("else objB"); synchronized(MyLock.objA){ System.out.println("else objA"); } } } }}
public class MyLock { //创建两把锁对象 public static final Object objA = new Object() ; public static final Object objB = new Object() ;}
34.演示:
/** * 分析: * 提供:资源对象:Student类 提供一些成员变量:姓名 和年龄 * 生产者线程:SetThread类: 生产一些学生数据(设置学生数据) * 消费者线程:GetThread类: 输出学生数据 * 测试类:StudentDemo类,实现多线程环境 * *使用刚才的这几个类:模拟生产消费者模式,生产者SetThread产生学生数据,而GetThread消费者线程输出学生数据,发现一个问题, *问题: * 输出学生数据的时候,是null--0 * * 对于每一个线程都在创建自己的学生对象,两个线程操作的两个对象而不是同一个对象,所以应该解决: * 将学生对象成员变量,然后通过构造方法进行传递,在测试类中,创建学生对象(同一个资源对象)让多个线程对这个学生对象进行操作 * * *改进:为了数据多并且效果更明显,加入循环语句进行操作,给生产者线程和消费者线程分别加入循环语句(while循环) */public class StudentDemo { public static void main(String[] args) { //创建一个资源对象 Student s = new Student() ; //创建资源对象 SetThread st = new SetThread(s) ; GetThread gt = new GetThread(s) ; //创建线程类对象 Thread t1 = new Thread(st) ; Thread t2 = new Thread(gt) ; //分别启动线程 t1.start() ; t2.start() ; }}
//生产者线程public class SetThread implements Runnable { private Student s ; public SetThread(Student s){ this.s = s ; } @Override public void run() { //设置学生数据 s.name = "高圆圆" ; s.age = 27 ; }}
//消费者线程public class GetThread implements Runnable { private Student s ; public GetThread(Student s){ this.s = s ; } @Override public void run() { //输出语句 System.out.println(s.name+"---"+s.age); }}
public class Student { String name; int age ;}
35.演示:
/** * 分析: * 提供:资源对象:Student类 提供一些成员变量:姓名 和年龄 * 生产者线程:SetThread类: 生产一些学生数据(设置学生数据) * 消费者线程:GetThread类: 输出学生数据 * 测试类:StudentDemo类,实现多线程环境 * *使用刚才的这几个类:模拟生产消费者模式,生产者SetThread产生学生数据,而GetThread消费者线程输出学生数据,发现一个问题, *问题: * 输出学生数据的时候,是null--0 * * 对于每一个线程都在创建自己的学生对象,两个线程操作的两个对象而不是同一个对象,所以应该解决: * 将学生对象成员变量,然后通过构造方法进行传递,在测试类中,创建学生对象(同一个资源对象)让多个线程对这个学生对象进行操作 * * *改进:为了数据多并且效果更明显,加入循环语句进行操作,给生产者线程和消费者线程分别加入循环语句(while循环) * 改进之后出现两个问题: * 1)同一个数据打印多次 * //CPU一点点时间片足够执行很多次 * 2)并且年龄和姓名不符合 * 线程的随机性导致的 * *优化改进之后,这些问题就说明当前多线程有安全问题: * 多线程安全问题的标准: * 1)是否是多线程环境 * 2)是否有共享数据 * 3)是否有多条语句对共享数据操作 * * 当前是多线程环境 * 有共享数据 * 有多条语句对共享数据:s.name,s.age *使用同步机制来解决这个问题:将多条语句对共享数据进包装 * *使用同步机制去解决线程的安全问题,但是又有一个新的问题: * 测试的时候,数据打印一打一大片,体验不好! *需求: * 让这个数据依次进行打印控制台,要使用这种方式去解决,利用Java等待唤醒机制 * * */public class StudentDemo { public static void main(String[] args) { //创建一个资源对象 Student s = new Student() ; //创建资源对象 SetThread st = new SetThread(s) ; GetThread gt = new GetThread(s) ; //创建线程类对象 Thread t1 = new Thread(st) ; Thread t2 = new Thread(gt) ; //分别启动线程 t1.start() ; t2.start() ; }}
//生产者线程public class SetThread implements Runnable { private Student s ; public SetThread(Student s){ this.s = s ; } private int x = 0 ; @Override public void run() { while(true){ //同步机制进行操作 synchronized (s) { if(x%2==0){ //x = 0 //设置学生数据 s.name = "高圆圆" ; //记录name的值是高圆圆 s.age = 38 ; }else{ //设置学生数据 s.name = "张杨" ; s.age = 27 ; //age = 27 ; } x ++ ;//1 } } }}
//消费者线程public class GetThread implements Runnable { private Student s ; public GetThread(Student s){ this.s = s ; } @Override public void run() { while(true){ synchronized (s) { //输出语句 //高圆圆 System.out.println(s.name+"---"+s.age); //高圆圆--27 //高圆圆-27 } } }}
public class Student { String name; int age ;}
36.演示:
/** * 分析: * 提供:资源对象:Student类 提供一些成员变量:姓名 和年龄 * 生产者线程:SetThread类: 生产一些学生数据(设置学生数据) * 消费者线程:GetThread类: 输出学生数据 * 测试类:StudentDemo类,实现多线程环境 * *使用刚才的这几个类:模拟生产消费者模式,生产者SetThread产生学生数据,而GetThread消费者线程输出学生数据,发现一个问题, *问题: * 输出学生数据的时候,是null--0 * * 对于每一个线程都在创建自己的学生对象,两个线程操作的两个对象而不是同一个对象,所以应该解决: * 将学生对象成员变量,然后通过构造方法进行传递,在测试类中,创建学生对象(同一个资源对象)让多个线程对这个学生对象进行操作 * * *改进:为了数据多并且效果更明显,加入循环语句进行操作,给生产者线程和消费者线程分别加入循环语句(while循环) * 改进之后出现两个问题: * 1)同一个数据打印多次 * //CPU一点点时间片足够执行很多次 * 2)并且年龄和姓名不符合 * 线程的随机性导致的 * *优化改进之后,这些问题就说明当前多线程有安全问题: * 多线程安全问题的标准: * 1)是否是多线程环境 * 2)是否有共享数据 * 3)是否有多条语句对共享数据操作 * * 当前是多线程环境 * 有共享数据 * 有多条语句对共享数据:s.name,s.age *使用同步机制来解决这个问题:将多条语句对共享数据进包装 * *使用同步机制去解决线程的安全问题,但是又有一个新的问题: * 测试的时候,数据打印一打一大片,体验不好! *需求: * 让这个数据依次进行打印控制台,要使用这种方式去解决,利用Java等待唤醒机制 * * */public class StudentDemo { public static void main(String[] args) { //创建一个资源对象 Student s = new Student() ; //创建资源对象 SetThread st = new SetThread(s) ; GetThread gt = new GetThread(s) ; //创建线程类对象 Thread t1 = new Thread(st) ; Thread t2 = new Thread(gt) ; //分别启动线程 t1.start() ; t2.start() ; }}
//生产者线程public class SetThread implements Runnable { private Student s ; public SetThread(Student s){ this.s = s ; } private int x = 0 ; @Override public void run() { while(true){ //同步机制进行操作 synchronized (s) { //判断有没有数据 if(s.flag){ //处于等待状态 try { s.wait() ;//阻塞式方法,立即释放锁 } catch (InterruptedException e) { e.printStackTrace(); } } if(x%2==0){ //x = 0 //设置学生数据 s.name = "高圆圆" ; //记录name的值是高圆圆 s.age = 38 ; }else{ //设置学生数据 s.name = "张杨" ; s.age = 27 ; //age = 27 ; } x ++ ;//1 //修改标记 s.flag = true ;//有数据了 //通知t2:消费者线程来消费数据 s.notify() ;//唤醒等待这种状态... } } }}
//消费者线程public class GetThread implements Runnable { private Student s ; public GetThread(Student s){ this.s = s ; } @Override public void run() { while(true){ synchronized (s) { //判断有没有数据 if(!s.flag){ try { s.wait() ;//调用的时候,会立即释放锁 } catch (InterruptedException e) { e.printStackTrace(); } } //输出语句 //高圆圆 System.out.println(s.name+"---"+s.age); //高圆圆--27 //高圆圆-27 //修改标记 s.flag = false ;//消费者线程 //通知对方(t1线程),消费者线程没有数据类,赶紧来消费 s.notify() ;//唤醒t1线程.... } } }}
public class Student { String name; int age ; //声明一个变量 boolean flag ; //默认没有数据,如果true,则说明有数据}
37.线程组表示一个线程的集合:Java允许一个线程中有多个线程
38.演示:
public class ThreadGroupDemo { public static void main(String[] args) {// method1() ; method2() ; } //给每一个子线程可以设置线程名称 private static void method2() { //如何设置线程组名称呢?// public ThreadGroup(String name)构造一个新线程组 ThreadGroup tg = new ThreadGroup("这是一个新的线程组") ; //创建线程类对象,并且将线程组对象作为参数进行传递,就使用Thread类的构造方法 //public Thread(ThreadGroup group,Runnable target ,String name){} //创建资源对象 MyRunnable my = new MyRunnable() ; Thread t1 = new Thread(tg, my, "线程1") ; Thread t2 = new Thread(tg, my, "线程2") ; ThreadGroup tg1 = t1.getThreadGroup() ; ThreadGroup tg2 = t2.getThreadGroup() ; System.out.println(tg1.getName()); System.out.println(tg2.getName()); tg.setDaemon(true) ;//将线程组中的所有的线程都设置为守护线程(后台线程) } private static void method1() { //如何获取多个线程所在的线程组名称呢? //创建资源对象 MyRunnable my = new MyRunnable() ; //创建线程类对象 Thread t1 = new Thread(my) ; Thread t2 = new Thread(my) ; //启动线程// t1.start() ;// t2.start() ; //获取t1,和t2线程所在的线程名称? //获取线程组对象// public final ThreadGroup getThreadGroup()返回该线程所属的线程组 ThreadGroup tg1 = t1.getThreadGroup() ; ThreadGroup tg2 = t2.getThreadGroup() ; //public final ThreadGroup getThreadGroup()返回该线程所属的线程组 String name1 = tg1.getName() ; String name2 = tg2.getName() ; //子线程默认的线程组名称:main线程 System.out.println(name1); System.out.println(name2); //所有的线程它的默认线程组名称就是main System.out.println(Thread.currentThread().getThreadGroup().getName()); }}
public class MyRunnable implements Runnable { @Override public void run() { for(int x = 0 ; x <100 ; x ++){ System.out.println(Thread.currentThread().getName()+":"+x); } }}
39.演示:
/** * 分析: * 提供:资源对象:Student类 提供一些成员变量:姓名 和年龄 * 生产者线程:SetThread类: 生产一些学生数据(设置学生数据) * 消费者线程:GetThread类: 输出学生数据 * 测试类:StudentDemo类,实现多线程环境 * *使用刚才的这几个类:模拟生产消费者模式,生产者SetThread产生学生数据,而GetThread消费者线程输出学生数据,发现一个问题, *问题: * 输出学生数据的时候,是null--0 * * 对于每一个线程都在创建自己的学生对象,两个线程操作的两个对象而不是同一个对象,所以应该解决: * 将学生对象成员变量,然后通过构造方法进行传递,在测试类中,创建学生对象(同一个资源对象)让多个线程对这个学生对象进行操作 * * *改进:为了数据多并且效果更明显,加入循环语句进行操作,给生产者线程和消费者线程分别加入循环语句(while循环) * 改进之后出现两个问题: * 1)同一个数据打印多次 * //CPU一点点时间片足够执行很多次 * 2)并且年龄和姓名不符合 * 线程的随机性导致的 * *优化改进之后,这些问题就说明当前多线程有安全问题: * 多线程安全问题的标准: * 1)是否是多线程环境 * 2)是否有共享数据 * 3)是否有多条语句对共享数据操作 * * 当前是多线程环境 * 有共享数据 * 有多条语句对共享数据:s.name,s.age *使用同步机制来解决这个问题:将多条语句对共享数据进包装 * *使用同步机制去解决线程的安全问题,但是又有一个新的问题: * 测试的时候,数据打印一打一大片,体验不好! *需求: * 让这个数据依次进行打印控制台,要使用这种方式去解决,利用Java等待唤醒机制 * * *再次改进:(最终版代码) * 现在将资源对象Student中的成员变量私有化 * 并且给当前类中提供两个方法,同步方法 * 在两个线程:生产者线程和消费者中线程,注意调用这两个方法就可以了! * * */public class StudentDemo { public static void main(String[] args) { //创建一个资源对象 Student s = new Student() ; //创建资源对象 SetThread st = new SetThread(s) ; GetThread gt = new GetThread(s) ; //创建线程类对象 Thread t1 = new Thread(st) ; Thread t2 = new Thread(gt) ; //分别启动线程 t1.start() ; t2.start() ; }}
//生产者线程public class SetThread implements Runnable { private Student s ; public SetThread(Student s){ this.s = s ; } private int x = 0 ; @Override public void run() { while(true){ if(x %2 ==0){ s.set("高圆圆", 38) ; }else{ s.set("张杨", 27) ; } x ++ ; } }}
//消费者线程public class GetThread implements Runnable { private Student s ; public GetThread(Student s){ this.s = s ; } @Override public void run() { while (true) { // 调用方法 s.get(); } }}
public class Student { private String name; private int age ; //声明一个变量 private boolean flag ; //默认没有数据,如果true,则说明有数据 //set(String name,int age)方法,产生数据数据 public synchronized void set(String name, int age) { // 同步机制进行操作 // 判断有没有数据 if (this.flag) { // 处于等待状态 try { this.wait();// 阻塞式方法,立即释放锁 notify notifyAll(); } catch (InterruptedException e) { e.printStackTrace(); } } //设置学生数据 this.name = name ; this.age = age ; //修改标记 this.flag = true ;//有数据了 //通知t2:消费者线程来消费数据 this.notify() ;//唤醒等待这种状态... } public synchronized void get(){ if(!this.flag){ try { this.wait() ;//调用的时候,会立即释放锁 } catch (InterruptedException e) { e.printStackTrace(); } } //输出语句 //高圆圆 System.out.println(this.name+"---"+this.age); //修改标记 this.flag = false ;//消费者线程 //通知对方(t1线程),消费者线程没有数据类,赶紧来消费 this.notify() ;//唤醒t1线程.... }}
40.线程池的好处:节约成本,很多子线程调用完毕不会立即被回收掉,而是会回到线程池中被多次利用!
JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
public static ExecutorService newFixedThreadPool(int nThreads)
Executors工厂类中的这个方法参数直接指定在当前线程池中有多少个线程
这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnabl e对象或者Callable对象代表的线程。它提供了如下方法
ExecutorsService :接口中的方法
Future
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class ExecutorsDemo { public static void main(String[] args) { //创建线程池对象,使用Executors工厂类// public static ExecutorService newFixedThreadPool(int nThreads) ExecutorService pool = Executors.newFixedThreadPool(2) ; //下来使用ExecutorService(跟踪多个异步任务)一些方法 //使用submit(Runnable target):提交多个任务 pool.submit(new MyRunnable()) ; pool.submit(new MyRunnable()) ; //结束线程池// void shutdown() pool.shutdown() ; }}
public class MyRunnable implements Runnable { @Override public void run() { //for循环 for(int x = 0 ; x < 100 ; x ++){ System.out.println(Thread.currentThread().getName()+":"+x); } }}
42.多线程程序的实现方式3:(实际开发中很少用到!)
public static ExecutorService newFixedThreadPool(int nThreads)
Executors工厂类中的这个方法参数直接指定在当前线程池中有多少个线程
这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnabl e对象或者Callable对象代表的线程。它提供了如下方法
ExecutorsService :接口中的方法
Future submit(Callable task)
该返回值表示:异步计算的结果!
43.演示:
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class CallableDemo { public static void main(String[] args) { //创建线程池对象,利用工厂类 ExecutorService Threadpool = Executors.newFixedThreadPool(2) ; //提交Callable任务(异步任务) Threadpool.submit(new MyCallable()) ;//相当于线程中的start()方法 Threadpool.submit(new MyCallable()) ; //结束线程池 Threadpool.shutdown() ; }}
import java.util.concurrent.Callable;public class MyCallable implements Callable<Object> { //call()方法的返回值是和Callable中的泛型是一致的! @Override public Object call() throws Exception { for(int x = 0 ; x < 100 ; x ++){ System.out.println(Thread.currentThread().getName()+":"+x); } return null; }}
44.演示:
import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;public class CallableDemo { public static void main(String[] args) throws InterruptedException, ExecutionException { //创建线程池对象,利用工厂类:Executors ExecutorService ThreadPool = Executors.newFixedThreadPool(2) ; //提交2异步任务,分别计算1-100,1-200之间的和 Future<Integer> f1 = ThreadPool.submit(new MyCallable(100)) ; Future<Integer> f2 = ThreadPool.submit(new MyCallable(200)) ; //分别调用Future接口中 get()方法,返回具体的结果 Integer v1 = f1.get() ; Integer v2 = f2.get() ; //输出结果 System.out.println("v1:"+v1); System.out.println("v2:"+v2); }}
import java.util.concurrent.Callable;//线程求和public class MyCallable implements Callable<Integer> { private int number ; public MyCallable(int number){ this.number = number ; } @Override public Integer call() throws Exception { //定义最终结果变量 int sum = 0 ; for(int x = 1 ; x <=number; x ++ ){ sum += x ; } return sum; }}
45.JavaSe中的定时器: Timer:
常用的几个方法:
public void schedule(TimerTask task,Date time)安排在指定的时间执行指定的任务
public void schedule(TimerTask task, long delay)在多少毫秒后执行指定任务
public void schedule(TimerTask task, long delay, long period)
在多少毫秒后,执行任务,并且每个多少毫秒重复执行
public void cancel()终止此计时器,丢弃所有当前已安排的任务
46.演示:
需求:3秒后执行爆炸任务
import java.util.Timer;import java.util.TimerTask;public class TimerDemo { public static void main(String[] args) { //public Timer()创建一个新计时器。 Timer t = new Timer() ; //调用public void schedule(TimerTask task, long delay)在多少毫秒后执行指定任务 //TimerTask:需要被执行的任务类是一个抽象类,所以不能直接实例化 t.schedule(new MyTask(t), 3000) ; }}//定义一个任务类:MyTask//TimerTask中的一个抽象方法:public abstract void run():执行定时器的任务class MyTask extends TimerTask{ private Timer t ; public MyTask(){ } public MyTask(Timer t){ this.t = t ; } @Override public void run() { System.out.println("bom,爆炸了..."); t.cancel() ;//取消任务... }}
import java.util.Timer;import java.util.TimerTask;/** * public void schedule(TimerTask task, long delay, long period) * 在多少毫秒后,执行任务,并且每个多少毫秒重复执行 * 需求:3秒后执行爆炸任务,每隔2秒重复爆炸 */public class TimerDemo2 { public static void main(String[] args) { //创建定时器对象 Timer t = new Timer() ; //调用方法 t.schedule(new MyTask2(), 3000, 2000) ; }}class MyTask2 extends TimerTask { @Override public void run() { System.out.println("bom,爆炸了...."); }}
46.演示:
import java.io.File;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Timer;import java.util.TimerTask;/** * 需求:在指定的时间删除我们的指定目录(你可以指定c盘,但是我不建议,我使用项目路径下的demo) *///删除的demo文件夹的任务class DeleteFolder extends TimerTask{ @Override public void run() { //封装当前项目下的这个demo文件 File srcFolder = new File("demo") ; deleteFolder(srcFolder) ; } //递归删除 private void deleteFolder(File srcFolder) { //获取当前srcFolder下面的所有的文件以及文件夹的File数组 File[] fileArray = srcFolder.listFiles() ; //对该对象非空判断 if(fileArray !=null){ //增强for遍历 for(File file :fileArray){ //继续判断file对象是否是文件夹 if(file.isDirectory()){ //继续回到删除目录的方法 deleteFolder(file) ; }else{ //不是目录,是文件,直接删除 System.out.println(file.getName()+"---"+file.delete()); } } System.out.println(srcFolder.getName()+"----"+srcFolder.delete()); } }}public class TimerTest { public static void main(String[] args) throws ParseException { //创建一个定时器对象 Timer t = new Timer() ; //public void schedule(TimerTask task,Date time)安排在指定的时间执行指定的任务 //定义一个文本日期格式 String dateStr = "2017-12-3 15:50:00" ; //解析成Date对象 //创建SimpleDateFormat对象 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") ; //解析方法 Date date = sdf.parse(dateStr) ; //调用定时器的这个在规定时间内执行某个任务的方法 t.schedule(new DeleteFolder(), date) ; }}
47.演示:
import java.io.File;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Timer;import java.util.TimerTask;/** * 需求:在指定的时间删除我们的指定目录(你可以指定c盘,但是我不建议,我使用项目路径下的demo) *///删除的demo文件夹的任务class DeleteFolder extends TimerTask{ @Override public void run() { //封装当前项目下的这个demo文件 File srcFolder = new File("demo") ; deleteFolder(srcFolder) ; } //递归删除 private void deleteFolder(File srcFolder) { //获取当前srcFolder下面的所有的文件以及文件夹的File数组 File[] fileArray = srcFolder.listFiles() ; //对该对象非空判断 if(fileArray !=null){ //增强for遍历 for(File file :fileArray){ //继续判断file对象是否是文件夹 if(file.isDirectory()){ //继续回到删除目录的方法 deleteFolder(file) ; }else{ //不是目录,是文件,直接删除 System.out.println(file.getName()+"---"+file.delete()); } } System.out.println(srcFolder.getName()+"----"+srcFolder.delete()); } }}public class TimerTest { public static void main(String[] args) throws ParseException { //创建一个定时器对象 Timer t = new Timer() ; //public void schedule(TimerTask task,Date time)安排在指定的时间执行指定的任务 //定义一个文本日期格式 String dateStr = "2017-12-3 15:50:00" ; //解析成Date对象 //创建SimpleDateFormat对象 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") ; //解析方法 Date date = sdf.parse(dateStr) ; //调用定时器的这个在规定时间内执行某个任务的方法 t.schedule(new DeleteFolder(), date) ; }}
- [2017.12.02-03]多线程
- 多线程02--多线程安全问题
- 03 多线程
- 多线程02
- 多线程学习02-多线程概念
- 2017.12.04 多线程
- 2017.12.12 java多线程
- 多线程11天--03
- 多线程12天--03
- 07---多线程03
- 03 Java多线程
- 2014/03/11 多线程
- 多线程03---NSThread
- Python 多线程学习03
- java多线程系列03
- day10_python多线程03
- 多线程基础03
- 多线程11天-02
- 【python 处理亿级数据】使用 Pandas 处理亿级数据
- 局部内部类里面的方法如何调用类所在方法里面的变量
- ajax跨域请求传输cookie
- 宽字节SQL注入原理
- java 设计模式
- [2017.12.02-03]多线程
- 进程间通信方式
- 开发高并发系统时保护系统的三把利器:缓存、降级和限流
- SharePoint Framework 自定义field示例中的gulp server运行错误
- Linux用户管理 (实验2)
- 高并发秒杀系统
- Flume + Keepalived构建高可用分布式采集系统
- js截取字符串,去掉前几位
- bzoj 4198: [Noi2015]荷马史诗 k叉哈夫曼树