Java多线程(全)学习笔记(上)

3 篇文章 0 订阅
3 篇文章 0 订阅

资源下载地址:http://download.csdn.net/detail/cloudyxuq/3763101

一.线程的创建和启动

java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。每条线程的作用是完成一定的任务,实际上就是执行一段程序流(一段顺序流的代码)。Java使用run方法来封装这样一段程序。

1.继承Thread类创建线程类

/**继承Thread来创建线程类*/
public class FirstThread extends Thread {
private int i;
//重写run方法,run方法的方法体就是线程执行体
public void run() {
for(;i<10;i++){
System.out.println(this.getName()+":"+i);
}
}
public static void main(String []args){
for(int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+"             .."+i);
if(i==10){
System.out.println("--------------------------------------------");
new FirstThread().start();
new FirstThread().start();
System.out.println("---------------------------------------------");
}
}
}
}
结果:红色部分每次运行都不一致,因为多线程也是并发的
main             ..0
main             ..1
main             ..2
main             ..3
main             ..4
main             ..5
main             ..6
main             ..7
main             ..8
main             ..9
main             ..10
--------------------------------------------
Thread-0:0
---------------------------------------------
Thread-1:0
Thread-1:1
Thread-1:2
Thread-1:3
Thread-0:1
Thread-1:4
Thread-1:5
main             ..11
Thread-1:6
Thread-1:7
Thread-1:8
Thread-1:9
Thread-0:2
Thread-0:3
main             ..12
main             ..13
......


总结 :从上面结果可以看出Thread-0和Thread-1两条线程输出的i变量都不连续(注意:i变量是FirestThread的实例属性,而不是局部变量,但因为程序每次创建线程都会创建一个FirstThread对象,所以Thread-0和Thread-1不能共享该实例属性)。

使用继承Thread类的方法来创建线程类,多条线程之间无法共享线程类的实例变量。

2.实现Runnable接口创建线程类

public class SecondThread implements Runnable {
private int i;
public void run() {
for(;i<20;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
public static void main(String [] args){
for(int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+"             .."+i);
if(i==10){
SecondThread st=new SecondThread();
//通过new Thread( Runable target,String name)来创建新线程
new Thread(st,"线程1").start();
new Thread(st,"线程2").start();
}
}
}
结果:红色部分每次运行都不一致,因为多线程也是并发的
main             ..0
main             ..1
main             ..2
main             ..3
main             ..4
main             ..5
main             ..6
main             ..7
main             ..8
main             ..9
main             ..10
--------------------------------------------
线程1:0
--------------------------------------------
线程1:1
线程2:1
线程2:3
main             ..11
线程2:4
线程2:5
线程2:6
线程1:2
线程2:7
线程2:9
线程2:10
线程2:11
线程2:12
线程2:13
main             ..12
线程2:14
线程2:15
线程2:16
线程2:17
线程1:8
线程2:18
main             ..13
main             ..14
线程1:19
main             ..15
main             ..16
main             ..17
。。。。


总结:根据源代码中Thread类构造方法  Ruanalbe接口对象target只能作为参数传递到Thread构造方法中,所以多个线程可以共用一个Runnable对象,因为都用同一个Runnable对象所以在Runnable实现类的实例变量也可以共享了。

所以Runable非常适合多个相同线程来处理同一份资源的情况。

二.线程的生命周期

   1.New新建 :当线程被创建时,该线程处于新建状态,此时它和其他java对象一样,仅仅由Java虚拟机为其分配了内存,并初始化了其成员变量的值。(此时的线程没有表现出任何表现出任何线程的动态特征,程序也不会执行线程的线程执行体)new Thread()||new Thread(Runnable target,String name)。

   2.Runnable就绪:就绪也就是说启动线程,但是启动线程使用start方法,而不是run方法!永远不要调用线程对象的run()方法!调用start方法来启动线程,系统会将该run方法当成线程执行体来处理。如果直接调用线程对象的run方法。则run方法会立即执行,且在这个run方法的执行体未执行结束前其他线程无法并发执行(即系统会将run方法当做一个普通对象的普通方法,而不是线程执行体对待)

      附1:如果有一个主线程,一个子线程。当根据逻辑代码该调用子线程时不一定会立即调用,为了想在子线程start()后立即调用子线程,可以考虑使用Thread.sleep(1),这样会让当前线程(主线程)睡眠1毫秒,因为cpu在这1毫秒中是不会休息的,这样就会去执行一条处于就绪状态的线程。

      附2:不能对已经处于就绪状态的线程,再次使用start()

3.Running 运行:当处于就绪状态时,该线程获得cpu,执行体开始运行,就处于运行状态了。

4.Blocked 阻塞:线程不可能一直处于运行状态(线程执行体足够短,瞬间就可以完成的线程排除),线程会在运行过程中需要被中断,因为是并发,目的是会让其他线程获得执行的机会,线程的调度细节取决于OS采用的策略。(抢占式调度xp win7 linux unix..)。如果是一些特殊的小型设备可能采用 协作式调度(只有线程自己调用它的sleep()或yield()才会放弃所占用的资源)。


5.Dead死亡:根据上图所示。测试测试某条线程是否已经死亡,可以调用线程对象的isAlive()方法,当线程处于就绪,运行,阻塞时,返回true。线程处于新建,死亡时返回false

不能对已经死亡的线程调用start()方法使它重新启动,死亡就是死亡,是不能再次作为线程执行的。

当主线程结束时候,其他线程不受任何影响,并不会随之结束。一旦子线程启动起来后,它就拥有和主线程相同的地位,它不会受到主线程的影响。

三.控制线程

1.join线程:

让一个线程等待另一个线程完成的方法:join()。当在某个程序执行流中调用其他线程的join()方法,那该执行流对应的线程就会阻塞,知道被join()加入的join线程完成为止。join方法通常有使用线程的程序调用,将大问题划分成许多小问题,每个小问题分配一个线程。当所有的小问题都得到处理后,再调用 主线程来进一步操作(Thread t=new Thread();t.start();t.join简单来说就是加入到t线程。等t线程执行完成后才会返回出来执行线程。

     Join方法有三种重用形式:

           Join():等待被join的线程执行完成

           Join(long millis):等待join线程的时间最长为millis毫秒,如果在这个时间内,被join的线程还没有执行结束则不再等待)

           Joinlong millisint nanos)千分之一毫秒(不用)

Code

public class JoinThread implements Runnable{
@Override
public void run() {
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
public static void main(String [] args) throws InterruptedException{
//实例化一个Runnable
JoinThread jt=new JoinThread();
//创建一个线程
new Thread(jt).start();
for(int i=0;i<10;i++){
if(i==3){
Thread th=new Thread(jt);
//启动第二个线程
th.start();
//main的线程中调用了th线程的join方法
//让第二个线程执行完成后再执行main
th.join();
}
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
结果:
Thread-0:0
Thread-0:1
Thread-0:2
main:0
main:1
Thread-0:3
main:2
Thread-0:4
Thread-1:0
Thread-1:1
Thread-1:2
Thread-1:3
Thread-1:4
main:3
main:4
main:5
main:6
main:7
main:8
main:9


2.后台线程:

Code:

public class DaemonThread implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
public static void main(String [] args){
//要将前台线程转换成后台线程,需要在该线程刚新建还未start()之前转换。main线程也是前台线程
//所有前台线程死亡时,后台线程也就随之死亡。
DaemonThread dt=new DaemonThread();
Thread td=new Thread(dt,"线程1");
System.out.println("main方法是否是后台线程"+Thread.currentThread().isDaemon());
System.out.println("td线程最初是否是后台线程"+td.isDaemon());
//指定td为后台线程
td.setDaemon(true);
System.out.println("td线程执行setDaemon方法后是否是后台线程"+td.isDaemon());
//就绪启动后台线程
td.start();
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
结果:只要前台线程结束,后台线程也会随之结束,并不是马上结束
main方法是否是后台线程false
td线程最初是否是后台线程false
td线程执行setDaemon方法后是否是后台线程true
main 0
main 1
线程1:0
线程1:1
main 2
线程1:2
线程1:3
main 3
线程1:4
线程1:5
main 4
线程1:6
线程1:7
线程1:8
线程1:9
线程1:10
线程1:11
线程1:12
线程1:13


3.线程睡眠:sleep

/**
 * 线程睡眠:sleep有两种重载形式:
 * static void sleep(long millis)
 * static void sleep(long millis,int nanos)
 *
 */
public class SleepThread {
public static void main(String [] args) throws InterruptedException{
for(int i=0;i<5;i++){
System.out.println("线程:"+Thread.currentThread().getName()+"当前时间:"+new Date());
//让当前线程暂停2秒
Thread.sleep(2000);
}
}
}
结果:
线程:main当前时间:Fri Nov 04 18:51:33 CST 2011
线程:main当前时间:Fri Nov 04 18:51:35 CST 2011
线程:main当前时间:Fri Nov 04 18:51:37 CST 2011
线程:main当前时间:Fri Nov 04 18:51:39 CST 2011
线程:main当前时间:Fri Nov 04 18:51:41 CST 2011


4.线程让步(yield

  

 /**
 * yield()方法是一个和sleep方法有点类似的静态方法。yield也可以让当前正在执行的线程暂停
 * 但它不会阻塞该线程,它只是将该线程转入就绪状态。yield只是让当前线程暂停一会儿,让系统的
 * 调度器重新调度一次(完全可能的情况是:当一个线程调用了yield方法暂停之后,线程调度器又马上
 * 将其调度出来重新执行。)
 * 实际上,当前线程调用了yield方法后,只有优先级和当前线程相同,甚至优先级高于当前线程的处于
 * 就绪状态的线程才会获得执行机会。
 *
 */
public class YieldThread implements Runnable{
@Override
public void run() {
for(int i=0;i<50;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
if(i==20){
Thread.yield();
}
}
}
public static void main(String [] args){
//启动第一条子线程
Thread td1=new Thread(new YieldThread(),"线程1");
//最高级
//td1.setPriority(Thread.MAX_PRIORITY);
//启动第二条子线程
Thread td2=new Thread(new YieldThread(),"线程2");
//最低级
td2.setPriority(Thread.MIN_PRIORITY);
td1.start();
td2.start();
System.out.println(Thread.currentThread().getName());
}
}

总结:sleepyield区别

A.sleep方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程的优先级。而yield只会给优先级>=当前优先级的线程执行机会

B.Sleep方法会将线程转入阻塞状态,知道经过阻塞时间才会转入就绪状态。而yield是不会将线程转入阻塞状态的,它只是强制当前线程进入就绪状态。

C.Sleep会抛出InterruptedException异常。而yield没有声明任何异常

D.Sleep方法比yield方法有更好的移植性。

E.通常不依靠yield来控制并发线程控制




  • 11
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
好的,下面是Java多线程编程学习笔记之十二:生产者—消费者模型的相关内容和代码。 ## 生产者—消费者模型简介 生产者—消费者模型是一种常见的多线程并发模型,它涉及到两个角色:生产者和消费者。生产者负责生产数据,消费者负责消费数据。生产者和消费者通过一个共享的缓冲区进行通信,生产者将数据放入缓冲区,消费者从缓冲区获取数据。 在多线程编程中,生产者—消费者模型的实现有多种方式,本文将介绍一种基于Java的实现方式。 ## 生产者—消费者模型的实现 ### 1. 定义共享缓冲区 共享缓冲区是生产者和消费者进行通信的桥梁,它需要实现以下功能: - 提供一个put方法,允许生产者将数据放入缓冲区; - 提供一个take方法,允许消费者从缓冲区获取数据; - 当缓冲区已满时,put方法应该等待; - 当缓冲区为空时,take方法应该等待。 以下是一个简单的共享缓冲区的实现: ```java public class Buffer { private int[] data; private int size; private int count; private int putIndex; private int takeIndex; public Buffer(int size) { this.data = new int[size]; this.size = size; this.count = 0; this.putIndex = 0; this.takeIndex = 0; } public synchronized void put(int value) throws InterruptedException { while (count == size) { wait(); } data[putIndex] = value; putIndex = (putIndex + 1) % size; count++; notifyAll(); } public synchronized int take() throws InterruptedException { while (count == 0) { wait(); } int value = data[takeIndex]; takeIndex = (takeIndex + 1) % size; count--; notifyAll(); return value; } } ``` 上面的Buffer类使用一个数组来表示缓冲区,size表示缓冲区的大小,count表示当前缓冲区中的元素数量,putIndex和takeIndex分别表示下一个可写和可读的位置。put和take方法都是同步方法,使用wait和notifyAll来进行线程间的等待和通知。 ### 2. 定义生产者和消费者 生产者和消费者都需要访问共享缓冲区,因此它们都需要接收一个Buffer对象作为参数。以下是生产者和消费者的简单实现: ```java public class Producer implements Runnable { private Buffer buffer; public Producer(Buffer buffer) { this.buffer = buffer; } public void run() { try { for (int i = 0; i < 10; i++) { buffer.put(i); System.out.println("Produced: " + i); Thread.sleep((int)(Math.random() * 1000)); } } catch (InterruptedException e) { e.printStackTrace(); } } } public class Consumer implements Runnable { private Buffer buffer; public Consumer(Buffer buffer) { this.buffer = buffer; } public void run() { try { for (int i = 0; i < 10; i++) { int value = buffer.take(); System.out.println("Consumed: " + value); Thread.sleep((int)(Math.random() * 1000)); } } catch (InterruptedException e) { e.printStackTrace(); } } } ``` 生产者在一个循环中不断地向缓冲区中放入数据,消费者也在一个循环中不断地从缓冲区中获取数据。注意,当缓冲区已满时,生产者会进入等待状态;当缓冲区为空时,消费者会进入等待状态。 ### 3. 测试 最后,我们可以使用下面的代码来进行测试: ```java public class Main { public static void main(String[] args) { Buffer buffer = new Buffer(5); Producer producer = new Producer(buffer); Consumer consumer = new Consumer(buffer); Thread producerThread = new Thread(producer); Thread consumerThread = new Thread(consumer); producerThread.start(); consumerThread.start(); } } ``` 在上面的代码中,我们创建了一个缓冲区对象和一个生产者对象和一个消费者对象,然后将它们分别传递给两个线程,并启动这两个线程。 运行上面的代码,我们可以看到生产者和消费者交替地进行操作,生产者不断地向缓冲区中放入数据,消费者不断地从缓冲区中获取数据。如果缓冲区已满或者为空,生产者和消费者会进入等待状态,直到缓冲区中有足够的空间或者有新的数据可用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值