@w460461339
2016-11-15T23:09:02.000000Z
字数 7218
阅读 1060
Java基础
妈蛋= - 今天因为安装机械手出错被批评了…自己怎么那么不细心。下次安装的时候千万不要图省事漏安装零件,老老实实一个一个的安装!
今天主要学习了一下多线程的定义,构造以及使用,下面直接开始吧。
首先,我们需要知道什么是进程。
打开任任务管理器后,我们发现里面有一个进程选项,可以查看当前开启的进程。因此,我们知道,进程就是目前正在运行的程序。
多进程概述:
正在运行的程序,是系统进行资源分配和清理使用的独立单位
每一个进程都有它自己的内存空间和系统资源
而对于一个类似于迅雷这样的程序,可以同时对多个目标进行下载,那么每一个下载都是一个单独的线程,因此每一个进程里面可以包含多线程。
多线程概述:
是进程中的单个顺序控制流,是一条执行语句;
一个进程如果只有一条执行路径,就是单线程的;
一个进程如果有多条志新路径,就是多线程的。
多进程的目的是提高CPU的使用率;
多线程的目的是让该进程更加重要,提高该进程使用CPU的使用率。
另外,多线程具有随机性, 不敢保证在哪个时刻是哪个线程抢到了CPU资源。
这就是,多线程。
![此处输入图片的描述][1]
让自定义类进程Thread类,并重写run方法,在run方法中放置需要在线程中跑的程序。在测试类中创建该自定义类的对象,并调用start方法即可(该方法在Thread中)
构造类
package myThread;//继承Thread类public class MyThreadDemo extends Thread {//重写run方法,将要在线程中运行的程序写入;//一般都是写入比较费时的程序@Overridepublic void run() {// TODO Auto-generated method stubfor(int i=0;i<200;i++){//getName方法,得到当前类的名字System.out.println(this.getName()+":"+i);}}}
测试类
package myThread;public class myThread_test {public static void main(String[] args) {//创建自定义类对象;(也是创建线程对象)//需要几个线程就创建几个对象MyThreadDemo mtd1=new MyThreadDemo();MyThreadDemo mtd2=new MyThreadDemo();//重置当前类的名字mtd1.setName("6666");mtd2.setName("7777");//开始线程,注意不能用runmtd1.start();mtd2.start();//获取主线程的名字//这里除了main主线程外,还有一个回收机制,//所以算上自己写的两个,应该有4个线程。System.out.println(Thread.currentThread().getName());}}
多线程调用CPU资源采用抢占式调用模型,优先级越高越有可能抢到,优先级相同就随机分配。(注意,不是优先级高就一定能够抢到的!)
另外,默认优先级是5,最低是1,最高是10,只能在这个范围内选取。
构造类同上
package myThread;public class myThread_test {public static void main(String[] args) {MyThreadDemo mtd1=new MyThreadDemo();MyThreadDemo mtd2=new MyThreadDemo();mtd1.setName("6666");mtd2.setName("7777");//输出默认优先级,为5System.out.println(mtd1.getPriority());//重新设置其优先级为10mtd1.setPriority(10);//结果可以发现,mtd1和mtd2仍然会交叉出现,//但mtd1明显在前面出现的多。mtd1.start();mtd2.start();System.out.println(Thread.currentThread().getName());}}
sleep方法,让当前线程暂停一定时间。因为针对当前线程,所以是写在构造类中。
构造类
package myThread;import java.util.Date;public class MyThreadDemo extends Thread {@Overridepublic void run() {// TODO Auto-generated method stubfor(int i=0;i<200;i++){System.out.println(this.getName()+":"+i);System.out.println(new Date());//由于覆盖了方法, 所以异常只能try catchtry {//休眠1000ms,当前线程进入阻塞状态。Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}
等待该线程终止。
构造类就是最简单的,没有sleep函数的那种
public class MyThreadJoin {//在调用join方法是抛出异常public static void main(String[] args) throws InterruptedException {//创建自定义对象MyThreadDemo myd1=new MyThreadDemo();MyThreadDemo myd2=new MyThreadDemo();MyThreadDemo myd3=new MyThreadDemo();//设置名字myd1.setName("Thread1");myd2.setName("Thread2");myd3.setName("Thread3");//开始Thread1myd1.start();//设置仅当Thread1走完了,才能走Thread2,以及3myd1.join();myd2.start();//设置仅当Thread2走完了才能走Thread3myd2.join();myd3.start();}}
暂停当前正在执行的线程对象,并执行其他线程。
//构造类public class MyThreadDemo extends Thread {@Overridepublic void run() {for(int i=0;i<200;i++){System.out.println(this.getName()+":"+i);Thread.yield();}}}
package myThread;public class MyThreadJoin {public static void main(String[] args) throws InterruptedException {MyThreadDemo myd1=new MyThreadDemo();MyThreadDemo myd2=new MyThreadDemo();myd1.setName("Thread1");myd2.setName("Thread2");myd1.start();myd2.start();}}
很大概率出现你一次我一次的情况
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
该方法必须在启动线程前调用。
意思是,在某个线程A里又调用的其他的线程B和C,设置B和C为A的后台线程,则当A结束时,B和C一定会马上结束。
package myThread;public class MysetDaemon {public static void main(String[] args) {MyThreadDemo myd1=new MyThreadDemo();MyThreadDemo myd2=new MyThreadDemo();myd1.setName("张飞");myd2.setName("关羽");//此时这些程序都在主线程里开启//myd1和myd2设置为主线程的后台(守护)线程//当主线程完了时,不管myd1和myd2是否走完了,都不会继续下一个//但会向前抽抽两下…myd1.setDaemon(true);myd2.setDaemon(true);Thread.currentThread().setName("刘备");for(int i=0;i<10;i++){System.out.println(Thread.currentThread().getName()+i);}myd1.start();myd2.start();}}
public class MyThreadDemo extends Thread {@Overridepublic void run() {//次数要够多,不然太少,会在抽抽的时候抽光,就看不出效果。for(int i=0;i<1000;i++){System.out.println(this.getName()+":"+i);}}}
比如在构造类中,要让当前线程睡10秒再走,但是在测试类中说,只能睡3秒,3秒之后就起。这个时候就需要用到interrupt
构造类
public class MyThreadDemo extends Thread {@Overridepublic void run() {try {Thread.sleep(30000);} catch (InterruptedException e) {System.out.println("线程被中断");}System.out.println("线程结束");}}
测试类
public class MyThreadInterrupt {public static void main(String[] args) {MyThreadDemo myd1=new MyThreadDemo();myd1.start();//开始只让睡3s,之后中断//此时构造类中有sleep仍在执行,因此interrupt向构造类中sleep抛出一个InterruptedException的异常,中断构造类中sleep//try {Thread.sleep(3000);myd1.interrupt();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
因此显示结果为:线程被中断,线程结束。
构造类
public class MyThreadDemo implements Runnable {@Overridepublic void run() {// TODO Auto-generated method stubfor(int i=0;i<1000;i++){//注意,由于实现了Runnable接口,所以不能直接利用this.getName来获取当前线程的名字//只能通过利用Tread.currentThread().getName()来得到System.out.println(Thread.currentThread().getName()+":"+i);}}}
测试类
public class MyTreadWay2 {public static void main(String[] args) {//只需要创建一个测试类对象MyThreadDemo myd=new MyThreadDemo();//就可以构造出许多的线程Thread td1=new Thread(myd,"Thread1");Thread td2=new Thread(myd,"Thread2");Thread td3=new Thread(myd,"Thread3");td1.start();td2.start();td3.start();}}
第二种创建多线程的方法比较好的实现了数据(一般放在run外面)和程序(放在run里面)的分离
需求:
三个售票窗口,总共一百张票,出完为止。
网络会出现一些延迟,需要添加延迟消除网络延迟影响。
要求:
不能重复卖票;
不能多卖票;
利用第一种方法和第二种方法实现多线程,来创建这个程序,无非就是成员变量是不是静态的问题。因为第二种方法能够更好的表现出数据和程序的分离,所以这里使用第二种方法。
构造类
package sellTicket;public class Ticket implements Runnable{private int ticket=100;@Overridepublic void run() {//这个while是为了保证有票就一直卖while(true){if(ticket>0){//延迟,消除网络延迟影响try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName()+"已经出售第"+(ticket--)+"张票");}}}}
测试类
package sellTicket;public class Sell {public static void main(String[] args) {Ticket tc=new Ticket();Thread td1=new Thread(tc,"售票窗口1");Thread td2=new Thread(tc,"售票窗口2");Thread td3=new Thread(tc,"售票窗口3");td1.start();td2.start();td3.start();}}
此时会出现重复卖票以及卖到第0张和第-1张票的情况。
A:重复卖票
原因:CPU的一次操作必须是原子性的。
B:出现了负数
原因:随机性和延迟导致的。
解决方案:
在A线程执行时,锁死该部分程序,直到A线程该部分程序结束,才能继续多个线程继续争抢。
构造类,测试类是一样的
package sellTicket;public class Ticket implements Runnable{private int ticket=100;private Object obj=new Object();@Overridepublic void run() {while(true){//利用synchronized保住所有需要锁住的代码//obj可以是任意类的任意对象,但必须定义在run外synchronized(obj){if(ticket>0){try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName()+"已经出售第"+(ticket--)+"张票");}}}}}
synchronized的格式:
synchronized(类的对象){
要锁住的代买
}
方法也可以被锁住:
private static synchronized long nextThreadID() {
return ++threadSeqNumber;
}
synchronized这个其实叫做同步代码块,锁住需要被锁住的代码内容。主要用来保证多线程的安全。具体的的深入了解还没有= -
线程是否有问题的判断标准:
A:是否是多线程环境
B:是否有共享数据(比如例子中成员变量ticket就是)
C:是否多条操作语句操作共享数据
如何解决问题:
基本思想:把多个语句操作共享数据的代码块锁起来,让任意时刻只能有一个线程执行这些代码块
同步的前提:
A:多线程
B:多个线程使用的是同一个锁对象(定义在run外的obj)
同步的好处以及弊端:
好处:解决了多线程安全问题;
弊端:线程太多时,会消耗大量资源
锁对象问题:
同步代码块:锁对象为任意类的任意对象
同步成员方法:锁对象为this,及本类的当前对象
同步静态方法/变量:锁对象为类的字节码对象(反射内容)
之前讲过,vector是线程安全的,ArrayList是线程不安全的。但是当我们需要线程安全时,也不用vector,是因为我们有更高效的方式。
synchronizedCollection(Collection< T > c);
返回指定 collection 支持的同步(线程安全的)collection。
synchronizedList(List< T > list) 返回指定列表支持的同步(线程安全的)列表。
……还有几个就不列了,那么可以通过这个来创建一个线程安全的ArrayList
List<String> list=Collections.synchronizedList(new ArrayList<String>());
(终于全部写完了…可以安心的玩一会了~)