Java se四大块之线程(上)

来源:互联网 发布:linux系统测试工具 编辑:程序博客网 时间:2024/05/23 23:38

一 程序 进程 线程

程序:就是静态的代码

进程:是正在运行的程序,系统会分配给他内存空间和资源,你打开两个软件时,其实在一个时间点上,只能有一个程序在cpu上运行,电脑是多核,但cpu的个数再多也不能多于进程数 ,你看着两个软件是在同时进行,实际上是电脑在频繁快速切换,一会运行这个程序一会运行另一个,就像是视频一样,其实只是图片,不过图片的快速切换,让你觉着他是一连串的动作。进程有自己的地址空间,进程具有动态性,并发性,独立性。进程的间的切换开销比较大,所以有时候当你想要切换程序时 会卡顿。

线程:进程内部的执行单元,是程序中一个单一的顺序控制流程,也被称为轻量级进程,如果在一个进程中同时运行多个线程,用来完成不同工作,则称之为多线程。线程可并发执行,共享进程资源。例如wrd文档,我在打印文档的同时还能编写其他 文档内容。线程间的调度开销比较小。

二 线程 单线程 多线程
线程:程序 的同一条执行路径
单线程:程序只有一条执行路径
多线程:程序有多条执行路径

多线程不能提高运行速度;当一个程序有多条执行路径(多线程),那么就有更多机抢占cpu,提高了处理任务的效率。举个不恰当的例子,小时候,我们被罚抄作业,一根笔写太慢,我们就一次拿着三根笔写,写的速度没变,但效率却大大提高。

三 并行 并发 java 如何实现多线程

并行:程序在同一时间点上进行,
并发:程序在同时(这个同时指的是时间段而不是时间点)执行,java的程序就是并发,

四 线程中的基本方法
之前写的代码,都是单线程,main方法就是一个主线程。
例一

   class Thread1 extends Thread{@Overridepublic void run(){while(true){System.out.println("乌龟领先中");}}}public class Thread2{public static void main(String []args){Thread1 thread1=new Thread1();Thread2 thread2=new Thread2();thread1.start();thread2.start();}}class Thread2 extends Thread{@Overridepublic void run(){while (true){System.out.println("兔子领先中");}}}上面的例子创建两个自定义线程  继承了Thread类,重写run方法,创建对象,调用线程的 start方法,start方法就会调用run方法执行线程。从上面的例子的执行结果,我们不难看出,线程间的执行顺序是不受我们控制的。每个线程 都有一个默认的名称。注意当前线程类的名称不是线程名称。我们可以通过this.getName()获得当前线程的名称,通过Thread.currentThread()得到当前线程。通过对象.setName();修改当前线程名称。将我们希望线程执行的代码放到run方法中,然后通过start方法来启动线程,start方法首先为线程的执行准备好系统资源,然后再去调用run方法。当某个类继承了Thread类之后,该类就叫做一个线程类。 线程的实现有两种方式,第一种方式是继承Thread类,然后重写run方法;第二种是实现Runnable接口,然后实现其run方法。下面我们会举一个第二种方式的例子。例二

public class ThreadTest2
{
public static void main(String[] args)
{
// Thread t1 = new Thread(new Runnable()
// {
// @Override
// public void run()
// {
// for(int i = 0; i < 100; i++)
// {
// System.out.println(“hello :” + i);
// }
// }
// });
//
// t1.start();

/*  Runnable MyThread=new MyThread();    Runnable MyThread2=new MyThread2();    Thread  thread=new Thread(MyThread);    thread.start();    thread=new Thread(MyThread2);    thread.start(); */

Thread thread=new Thread(new MyThread());
thread.start();
thread=new Therad(new MyThread2());
thread.tart();//这四句 相当于上面星花内六行。

}

}

class MyThread implements Runnable
{
@Override
public void run()
{
for(int i = 0; i < 100; i++)
{
System.out.println(“hello :” + i);
}
}
}

class MyThread2 implements Runnable
{
@Override
public void run()
{
for(int i = 0; i < 100; i++)
{
System.out.println(“welcome: ” + i);
}

}

}

例三  

public class TicketThread extends Thread{
private int ticNum=200;
@Override
public void run(){
while(ticNum>0){
System.out.println(Thread.currentThread.getName()+”窗口 卖出,”+ticNum+“张票”);
ticNum–;
}
}

}
class TestThread{
public static void main(String []args){

TicketThread th1=new TicketThread();
TicketThread th2=new TicketThread();
TicketThread th3=new TicketThread();
TicketThread th4=new TicketThread();
th1.start();
th2.start();
th3.start();
th4.start();

}

}

例四

class ThreadRunnable implements Runnable{
@Override
public void run(){
private int ticNum=200;

while(ticNum>0){
System.out.println(Thread.currentThread.getName()+”窗口 卖出,”+ticNum+“张票”);
ticNum–;
}

}

}
public class Test{
public static void main(String[]args){
TicketThread tic=new TicketThread();
Thread th1=new Thread(tic);
Thread th2 =new Thread(tic);
Thread th3=new Thread(tic);
Thread th4=new Thread(tic);
th1.start();
th2.start();
th3.start();
th4.start();

}
}

当几个线程共享一份资源的时候,应该使用Runnable接口比较好

五  线程的生命周期新生状态使用new关键字建立一个线程对象以后,就会被分配内存空间,调用start方法就会进入就绪状态就绪状态具备了运行条件,还没有分配到cpu,处于线程 就绪队列,等待系统为其分配cpu运行状态执行自己的run方法内的代码,直到等待因为某资源二阻塞或者完成任务而死亡,如果在给定时间内没有执行结束,就会被系统换下来回到等待执行状态。阻塞状态处于运行状态 下的线程在某些情况下,如执行了sleep方法,或者等待IO设备,将让出cpu并暂时停止自己的运行,进入阻塞状态。当阻塞原因解除后 ,该线程便会进入就绪状态死亡状态线程死亡的原因有三个:1、run()或者call()方法执行完成,线程正常结束;2、线程抛出一个未捕获的Exception或Error;3、直接调用该线程的stop()方法来结束该线程;一个线程在未正常结束之前, 被强制终止是很危险的事情. 因为它可能带来完全预料不到的严重后果. 所以你看到Thread.suspend, Thread.stop等方法都被Deprecated了.那么不能直接把一个线程搞挂掉, 但有时候又有必要让一个线程死掉, 或者让它结束某种等待的状态 该怎么办呢? 优雅的方法就是, 给那个线程一个中断信号, 让它自己决定该怎么办. 比如说, 在某个子线程中为了等待一些特定条件的到来, 你调用了Thread.sleep(10000), 预期线程睡10秒之后自己醒来, 但是如果这个特定条件提前到来的话, 你怎么通知一个在睡觉的线程呢? 又比如说, 主线程通过调用子线程的join方法阻塞自己以等待子线程结束, 但是子线程运行过程中发现自己没办法在短时间内结束, 于是它需要想办法告诉主线程别等我了. 这些情况下, 就需要中断. 中断是通过调用Thread.interrupt()方法来做的. 这个方法通过修改了被调用线程的中断状态来告知那个线程, 说它被中断了. 对于非阻塞中的线程, 只是改变了中断状态, 即Thread.isInterrupted()将返回true; 对于可取消的阻塞状态中的线程, 比如等待在这些函数上的线程, Thread.sleep(), Object.wait(), Thread.join(), 这个线程收到中断信号后, 会抛出InterruptedException, 同时会把中断状态置回为false.六   线程控制方法:线程的优先级底层代码定义好的有大中小对应1051三个等级,默认等级是5,也可以设置其他 数字。查找和修改线程优先级的方法在API里面已经定义好,可以直接调用。算了我还是提一句吧,Thread.currentThread().getPriority();  对象.setPriority(数字);优先级高低只是意味着获取调度的概率的高低。当一个优先级低的线程长时间没有被调度的时候,他的优先级会慢慢变高。Join方法:阻塞指定线程等到另一个线程完成之后在继续执行。

public class TestJoin extends Thread{
@Override
public void run(){
for(int i=0;i<10;i++){
System.out.println(i+”线程执行中”);

}

}

public static void main(String []args){

for(int i=0;i<10;i++){
System.out.println(“main”);
if(i==5){

TestJoin t=new TestJoin();
t.start();
t.join();//此处有异常

}}}}

sleep方法:使线程停止一段时间,处于阻塞状态。当调用sleep方法时,,就算没有其他等待执行的线程,当前线程也不会恢复执行。

public class TestSleep{

public static void main(String []args){
System.out.println(“预备”);

for(int i=3;i>0;i–){
System.out.println(i);
Thread.sleep(1000);//此处异常
System.out.println(“go”);

}

}

}

import java.util.Date;
public class TestSleep{
public static void main(String []args){

DateFormat s=new SimpleDateFormat(“hh:mm:ss”);
while(true){
Date now =new Date();
String strDate =s.format(now);
System.out.println(strDate);
Thread.sleep(1000);//异常加try catch

}
}

}

yield方法:让当前正在执行线程暂停;不是阻塞线程,而是让其进入到就绪状态。如果调用了yield方法,却没有其他等待执行的线程,该线程就会马上恢复执行。这是个很虚伪的方法,就是我先让你一下,然后我们都在就绪状态。结果第二次可能我又抢到了。手慢无。。。。。。。

public class TestYeild{
public static void main(String []args){

First f=new First();
Second s=new Second();
f.start();
s.start();

}
}

class First extend Thread{
public void run(){

for(int i=1;i<=10;i++){
System.out.println(“First”);
Thread.yield();

}

}

}

class Second extends Thread{
public void run (){
for(int i=1;i<=10;i++){
System.out.printl(“Second”);
Thread.yield();
}

}

}

后台线程(守护线程):     1.  后台线程会随着主程序的结束而结束,但是前台进程则不会;或者说,只要有一个前台线程未退出,进程就不会终止。(下面的例子会充分说明这一点);     2.  默认情况下,程序员创建的线程是用户线程;用setDaemon(true)可以设置线程为后台线程(必须是在它启动前设置);而用isDaemon( )则可以判断一个线程是前台线程还是后台线程;     3. jvm的垃圾回收器其实就是一个后台线程;     4. setDaemon函数必须在start函数之前设定,否则会抛出IllegalThreadStateException异常;

public class RunnableTest implements Runnable {
private String name;
private long delay;
public static void main(String args[]) {
RunnableTest r = new RunnableTest(“thread1”,200);
Thread t = new Thread(r);
t.setDaemon(true); //后台线程
t.start();
try {
System.in.read();
}
catch(Exception e) {

}
System.out.println(“end main”);
}
public RunnableTest(String name,long delay) {
this.name = name;
this.delay = delay;
}
public void run(){
try {
while(true) {
System.out.println(name);
Thread.sleep(delay);
}
}
catch(Exception e) {
System.out.println(“Exception”);
}
}
}
/*
测试结果:
case 1: t.setDaemon(truer); //后台线程
thread1
thread1
thread1
……

thread1
end main

case 2: t.setDaemon(false); //前台线程,相当于不调用setDaemon
thread1
thread1
thread1
……

thread1
end main
thread1
thread1
thread1
……
“`

这里写图片描述