0
点赞
收藏
分享

微信扫一扫

谈谈java中的线程(初级概念)


定义

关于进程与线程的定义 可参看一下这个介绍


​​http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html​​


在不细抠定义的情况下


我们可以认为 在操作系统里一个任务就是一个进程 像word,qq都可以看做一个进程.


另一方面如果这个进程内部的函数调用 就是一条线 那它就是单线程


如果有多条线 那就是多线程 而在这个进程内部 每一条执行的流程就叫做一个线程



谈谈java中的线程(初级概念)_i++


我们自己定义的线程

在自定义线程之前 我们先看看java里关于线程的一些类


主要有两个


一个是interface Runnable


里面只有一个方法run()


一个是class Thread


其实它也实现了Runnable接口(也就必须重写了run方法),同时它还有一个方法叫start


来我们看第一个例子 顺便讲讲start与run的区别

public class TestThread1 {
public static void main(String args[]) {
Runner1 r = new Runner1();
r.start();
//r.run();
for(int i=0; i<100; i++) {
System.out.println("Main Thread:------" + i);
}
}
}

class Runner1 extends Thread {
public void run() {
for(int i=0; i<100; i++) {
System.out.println("Runner1 :" + i);
}
}
}

运行的结果是 Main Thread..与Runner1...交替输出

这时候 就运行了两个线程 一个主线程 一个r线程
如果把r.start改成r.run那么就是先打出100个Runner1..然后才是100个Main Thread
为什么?
大家仔细看看如果我们调用r.run() 那不就是函数调用了么!!!
所以一句话我们自定义的线程必须实现run方法 但调用的时候得是start()!


再看另一种定义线程的方式

public class TestThread1 {
public static void main(String args[]) {
Runner1 r = new Runner1();
Thread t = new Thread(r);
t.start();

for(int i=0; i<50; i++) {
System.out.println("Main Thread:------" + i);
}
}
}


class Runner1 implements Runnable {
public void run() {
for(int i=0; i<50; i++) {
System.out.println("Runner1 :" + i);
}
}
}


两种定义线程的方式 我们选择哪一种?


选第二种 实现Runable接口的方式 


因为我们一旦继承了Thread类 就不能再继承别的类了


因此能继承类能实现接口的时候就选实现接口的方式 


*************************************************

以下为2016-04-12日补充


那么使用实现implement的方法,还有什么好处呢?


可以实现多个线程处理一个资源


什么意思?


请写一个买票的代码


SellTicket类里面有一个成员变量ticketCount是int型的,或者说是AtomicInteger


run方法不断的对ticketCount进行减一操作


如果实现runnable,那我new一个SellTicket放到不同的Thread里面就OK


可是如果是继承Thread类,那么我就得new多个SellTicket


那么ticketCount又是几个呢?


以上为2016-04-12日补充

*************************************************


 sleep interrupt stop flag

public class TestThread3{
public static void main(String args[]) {
Runner3 r = new Runner3();
Thread t = new Thread(r);
t.start();
}
}


class Runner3 implements Runnable {
public void run() {
for(int i=0; i<30; i++) {
if(i%10==0 && i!=0) {
try{
Thread.sleep(2000);
}catch(InterruptedException e){}
}
System.out.println("No. " + i);
}
}
}



看这个例子 它运行的结果是 先马上打印出0-9然后停两秒再打印出10-19...


Thread.sleep()就是让程序休眠一段时间 时间的长短由参数指定 单位为毫秒


不过要注意 sleep会抛出InterruptedException异常


public class TestInterrupt {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
try {Thread.sleep(10000);}
catch (InterruptedException e) {}
thread.interrupt();


}
}


class MyThread extends Thread {
boolean flag = true;
public void run(){
while(true){
System.out.println("==="+new Date()+"===");
try {
sleep(1000);
} catch (InterruptedException e) {
return;
}
}
}
}



这段代码的结果是 每隔一秒输出当前的时间 等运行了10秒后停止

代码写到这里的时候,如何脑子里蹦出一个问题,如果一个线程在sleep的时候,如果传递的参数是5000,那这个5000是什么意思?

第一:这个线程开始进入阻塞队列,当"自然时间(也就是说你看着自己的手表,来读时间)"过了5000毫秒后,这个线程再从阻塞队列出来进入就绪队列,等待时间片

第二:这个线程就睡着了,时间片来了,我继续睡着,cpu被我独占,即使我在睡觉,cpu也得在这等我

感觉好像应该是第一种情况

那么请证明之:

看代码


public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {

@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"已经结束了");

}
}).start();
}

}

上面代码运行的结果是,启动5s后,一瞬间打印出

Thread-xx已经结束了

上面的100句话

这说明是进入了阻塞队列,在线程睡觉的时候,jvm的调度器就完全不管那些睡觉的线程了

我们再看下面的代码

public class SleepTest implements Runnable {
private Object lock;

public SleepTest(Object lock){
this.lock=lock;
}
public static void main(String[] args) {
Object lock=new Object();
for (int i = 0; i < 10; i++) {
new Thread(new SleepTest(lock)).start();
}

}


@Override
public void run() {
synchronized (lock) {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}
}

结果是大概每隔1s,输出一个Thread-x


上面的两个例子说明

线程sleep的时候,释放了对cpu的控制,但是没有释放对锁的控制

关于sleep的更多知识,请参见

​​理解 Thread.Sleep 函数​​


大家也看到了 sleep本身可能会被"打扰" 就是interrupt 我们在主线程里调用了 thread.interrupt();


等于就抛出了异常 那么thread就只能执行catch里面的代码 return了

public class InterruptTest {  
public static void main(String[] args) throws InterruptedException {
MyThread t = new MyThread("MyThread");
t.start();
Thread.sleep(100);// 睡眠100毫秒
t.interrupt();// 中断t线程
}
}
class MyThread extends Thread {
int i = 0;
public MyThread(String name) {
super(name);
}
public void run() {
while(true) {// 死循环,等待被中断
System.out.println(getName() + getId() + "执行了" + ++i + "次");
}
}
}

程序会一直执行下去么?

会. 为什么?

intertupt只是打断睡眠,它不是终止进程

看下面这个

public class InterruptTest {  
public static void main(String[] args) throws InterruptedException {
MyThread t = new MyThread("MyThread");
t.start();
Thread.sleep(100);// 睡眠100毫秒
t.interrupt();// 中断t线程
}
}
class MyThread extends Thread {
int i = 0;
public MyThread(String name) {
super(name);
}
public void run() {
while(!isInterrupted()) {// 当前线程没有被中断,则执行
System.out.println(getName() + getId() + "执行了" + ++i + "次");
}
}
}



另外 Thread还有一个方法叫stop(已废弃) 看名字 我们就知道它能干什么了


不过他比interrupt还粗暴 interrupt毕竟还有一个catch呢 在结束之前 还能干点事 stop就完全没有做其他事的机会了


当然要结束这种"死循环"的线程 也不难


MyThread里面加一个Boolean型的flag 令其为true while(true)改成while(flag) 


想结束的时候 在主函数里让flag=false 即可



这里就是把interrupt改成两个setFlag. 当然对那个flag的设值的名字可以改为shutdown


join yield priority

public class TestJoin {
public static void main(String[] args) {
MyThread2 t1 = new MyThread2("abcde");
t1.start();
try {
t1.join();
} catch (InterruptedException e) {}

for(int i=1;i<=10;i++){
System.out.println("i am main thread");
}
}
}
class MyThread2 extends Thread {
MyThread2(String s){
super(s);
}

public void run(){
for(int i =1;i<=10;i++){
System.out.println("i am "+getName());
try {
sleep(300);
} catch (InterruptedException e) {
return;
}
}
}
}



通过查看这个代码的结果 就明白 join就是把线程合并 


如上 就是等ti的run执行完毕后 主线程再继续往下走 有点把线程调用看成函数调用的感觉


至于Thread的yield()方法就是线程主动放弃他所拥有的时间片 让其他线程使用 (当然只是放弃一次 下一次有了时间片 它就不放弃了)

代码如下


public class TestYield {
public static void main(String[] args) {
MyThread3 t1 = new MyThread3("t1");
MyThread3 t2 = new MyThread3("t2");
t1.start();
t2.start();
}
}
class MyThread3 extends Thread {
MyThread3(String s){super(s);}
public void run(){
for(int i =1;i<=40;i++){
System.out.println(getName()+": "+i);
if(i%10==0){
yield();
}
}
}
}



运行时会发现 每当一个线程打印出自己的名字和整10的序号的时候 下一个运行的都不是自己 yield() 它放弃了本次对时间片的使用


至于priority(优先级)

有两个方法setPriority getPriority

priority 从1到10 10为最高

priority越高 获得时间片的几率越大 

代码如下


public class TestPriority {
public static void main(String[] args) {
Thread t1 = new Thread(new T1());
Thread t2 = new Thread(new T2());
t1.setPriority(Thread.NORM_PRIORITY + 3);
t1.start();
t2.start();
}
}


class T1 implements Runnable {
public void run() {
for(int i=0; i<1000; i++)
System.out.println("T1: " + i);

}
}


class T2 implements Runnable {
public void run() {
for(int i=0; i<1000; i++)
System.out.println("------T2: " + i);

}
}


sleep与yield

线程Asleep的时候,下一个时间片可以给任何一个线程

线程Ayield的时候,本来A线程所拥有的时间片会在与线程A同优先级的线程间转换


举报

相关推荐

0 条评论