`
wuzq1223
  • 浏览: 24769 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

java 线程

    博客分类:
  • java
阅读更多
1、进程与线程
进程是程序的一次动态执行过程,它经历了从代码加载、执行到执行完毕的一个完整的过程,这个过程也是进程本身从产生、发展到最终消亡的过程。
多线程是实现并发机制的一种有效手段。进程和线程一样,都是实现并发的一个基本单位。

2、Java的多线程实现:
在Java中要实现多线程有两种方式:
1. 继承Thread类
2. 实现Runnable接口

2.1、继承Thread类
Thread类是Java.lang包中定义的,一个类只要继承了Thread类,此类就称为多线程操作类,在Thread子类中,必须明确覆写Thread类中的run()方法,此方法为线程的主体,语法如下:
Class 类名称 extends Thread{ //继承Thread类
属性…; //类中定义的属性
方法…; //类中定义的方法
public void run(){ //覆写Thread类中的run()方法,此方法是线程的主体
线程主体;
}
}
java.lang包会在程序运行时自动导入,无需手工编写import语句。但是要想启动一个线程必须使用Thread类中定义的start()方法,一旦调用start()方法,实际上最终调用的就是run()方法。
class MyThread extends Thread //继承Thread类,作为线程的操作类
{
private String name;
public MyThread(String name){ //通过构造方法配置name属性
this.name=name;
}
public void run(){ //覆写run()方法,作为线程的操作主体
for(int i=0;i<10;i++){
System.out.println(name+"运行,i="+i);
}
}
}
public class Demo01
{
public static void main(String args[]){
MyThread mt1=new MyThread("线程1"); //实例化对象
MyThread mt2=new MyThread("线程2"); //实例化对象
mt1.start(); //启动线程主体
mt2.start(); //启动线程主体
}
}

2.2、实现Runnable接口
在Java中也可以通过实现Runnable接口的方式实现多线程,Runnable接口中只定义了一个抽象方法:public void run();
通过Runnable接口实现多线程:
Class 类名称 implements Runnable{ //实现Runnable接口
属性…; //类中定义的属性
方法…; //类中定义的方法
Public void run(){ //覆写Runnable接口中的run()方法
线程主体;
}
}

如果要想启动线程则肯定要依靠Thread类中的run()方法,在Thread类中提供了两个构造方法:
Public Thread(Runnable target);
Public Thread(Runnable target,String name);
这两个构造方法都可以接收Runnable的子类实例对象。
class MyThread implements Runnable //继承Thread类,作为线程的操作类
{
private String name;
public MyThread(String name){ //通过构造方法设置属性内容
this.name=name;
}
public void run(){ //覆写Runnable接口中的run()方法,作为线程的操作主体
for(int i=0;i<10;i++){
System.out.println(name+"运行,i="+i);
}
}
}
public class Demo02
{
public static void main(String args[]){
MyThread mt1=new MyThread("线程1"); //实例化Runnable子类对象
MyThread mt2=new MyThread("线程2"); //实例化Runnable子类对象
Thread t1=new Thread(mt1); //实例化Thread类对象
Thread t2=new Thread(mt2); //实例化Thread类对象
t1.start(); //启动线程
t2.start(); //启动线程
}
}

2.3、Thread类和Runnable接口联系
Thread类的定义:
Public class Thread extends Object implements Runnable
可以发现Thread类也是Runnable的子类。

2.4、Thread类和Runnable接口区别
使用Thread类在操作多线程的时候无法达到资源共享的目的,而Runnable接口实现多线程操作可以实现资源共享。

运行下面程序可以发现,一共卖掉了15张票,即三个线程各卖各的5张票,也就是说并没有达到资源共享的目的。
class MyThread extends Thread //继承Thread类,作为线程的操作类
{
private String name;
private int ticket=5; //表示有5张票
public MyThread(String name){
this.name=name;
}
public void run(){ //覆写run()方法,作为线程的操作主体
for(int i=0;i<10;i++){
if(ticket>0){
System.out.println(name+" 还有 "+ticket+" 张票!");
ticket--;
}
}
}
}
public class Demo03
{
public static void main(String args[]){
MyThread mt1=new MyThread("线程001"); //实例化对象
MyThread mt2=new MyThread("线程002"); //实例化对象
MyThread mt3=new MyThread("线程003"); //实例化对象
mt1.start(); //启动线程主体
mt2.start(); //启动线程主体
mt3.start(); //启动线程主体
}
}

运行下面程序可以发现,一共卖掉了5张票,即三个线程各卖各的5张票,也就是说达到资源共享的目的。
class MyThread implements Runnable //继承Thread类,作为线程的操作类
{
private String name;
private int ticket=5;
public MyThread(String name){ //通过构造方法设置属性内容
this.name=name;
}
public void run(){ //覆写Runnable接口中的run()方法,作为线程的操作主体
for(int i=0;i<10;i++){
if(ticket>0){
System.out.println(name+" 还有 "+ticket+" 票!");
ticket--;
}
System.out.println("线程");
}
}
}
public class Demo04
{
public static void main(String args[]){
MyThread mt=new MyThread("线程001"); //实例化Runnable子类对象
new Thread(mt).start();
new Thread(mt).start();
new Thread(mt).start();
//Thread t1=new Thread(mt); //实例化Thread类对象
//Thread t2=new Thread(mt); //实例化Thread类对象
//Thread t3=new Thread(mt); //实例化Thread类对象
//t1.start(); //启动线程
//t2.start(); //启动线程
//t3.start(); //启动线程
}
}

3、线程的状态
多线程在操作中也是有一个固定的操作状态的:
1. 创建状态:准备好了一个多线程的对象,Thread t =new Thread();
2. 就绪状态:调用了start()方法,等待CPU进行调用
3. 运行状态:执行run()方法
4. 阻塞状态:暂时停止执行,可能将资源交给其他线程使用
5. 终止状态(死亡状态):线程执行完毕了,不再使用了
`实际上调用start()方法的时候不是立刻启动的,而是要等待CPU进行调度。


4、线程的常用操作方法
在多线程中所有的操作方法实际上都是从Thread类开始的,所有的操作基本上都在Thread类之中。

4.1、取得和设置线程名称
在Thread类中,可以通过getName()方法取得线程的名称,通过setName()方法设置线程的名称。
线程的名称一般在启动线程前设置,但也允许为已经运行的线程设置名称。允许两个Thread对象有相同的名称,但为了清晰,应尽量避免这种情况的发生。
另外,如果程序并没有为线程指定名称,则系统会自动的为线程分配一个名称。
class MyThread implements Runnable //实现Runnable接口
{
public void run(){ //覆写run()方法
for(int i=0;i<3;i++){
System.out.println(Thread.currentThread().getName()+"运行,i="+i); //取得当前线程的名称
}
}
}
public class ThreadNameDemo
{
public static void main(String args[]){
MyThread mt=new MyThread(); //实例化Runnable子类对象
//通过Thread类的构造方法启动线程,并由系统自动设置线程名称
new Thread(mt).start();
//通过Thread类的构造方法启动线程,并手工设置线程名称
new Thread(mt,"线程001").start();
}
}

运行下面的程序发现,程序中由主方法直接通过线程对象调用里面的run()方法,所输出的结果中包含了一个“main”,此线程是由“mt.run()“产生,因为调用此语句是由主方法完成的,也就是说实际上主方法本身也是一个线程——主线程。
实际上,每当一个Java程序执行的时候,实际上都会启动一个JVM,每一个JVM实际上就是在操作系统中启动了一个进程,Java本身具备了垃圾回收机制,所以Java运行时至少启动两个线程:主线程、GC(垃圾回收)
class MyThread implements Runnable //实现Runnable接口
{
public void run(){ //覆写run()方法
for(int i=0;i<3;i++){
System.out.println(Thread.currentThread().getName()+"运行,i="+i); //取得当前线程的名称
}
}
}
public class ThreadNameDemo02
{
public static void main(String args[]){
MyThread mt=new MyThread(); //实例化Runnable子类对象
//通过Thread类的构造方法启动线程,并手工设置线程名称
new Thread(mt,"线程001").start();
mt.run(); //直接调用run()方法
}
}

4.2、判断线程是否在执行
通过Thread类之中的start()方法通知 CPU这个线程已经准备好启动,之后就等待分配CPU资源,运行此线程了。
在Java中可以使用isAlive()方法来测试线程是否已经启动或在是否还在执行。
class MyThread implements Runnable //实现Runnable接口
{
public void run(){ //覆写run()方法
for(int i=0;i<3;i++){
System.out.println(Thread.currentThread().getName()+"运行,i="+i); //取得当前线程的名称
}
}
}
public class ThreadAliveDemo01
{
public static void main(String args[]){
MyThread mt=new MyThread(); //实例化Runnable子类对象
//通过Thread类的构造方法启动线程,并手工设置线程名称
Thread t=new Thread(mt,"线程001"); //实例化Thread对象
System.out.println("线程开始执行之前:"+t.isAlive()); //判断线程是否启动
t.start();
System.out.println("线程开始执行之后:"+t.isAlive()); //判断线程是否启动
for(int i=0;i<3;i++){
System.out.println("main运行:"+i);
}
//一线的输出结果不确定,因为for循环执行完毕后,线程不一定已经执行完了
System.out.println("for循环执行之后:"+t.isAlive()); //判断线程是否启动
}
}


4.3、线程的强制执行
在线程操作中,可以使用join()方法让一个线程强制执行,线程强制执行期间,其他线程无法运行,必须等待此线程完成之后才可以继续执行。
class MyThread implements Runnable //实现Runnable接口
{
public void run(){ //覆写run()方法
for(int i=0;i<50;i++){
System.out.println(Thread.currentThread().getName()+" 运行,i="+i); //取得当前线程的名称
}
}
}
public class ThreadJoinDemo01
{
public static void main(String args[]){
MyThread mt=new MyThread(); //实例化Runnable子类对象
//通过Thread类的构造方法启动线程,并手工设置线程名称
Thread t=new Thread(mt,"线程001"); //实例化Thread对象
t.start();
for(int i=0;i<50;i++){
if(i<10){
try{
t.join(); //线程强制执行
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println("main运行:"+i);
}
}
}

4.4、线程的休眠
在程序中允许一个线程进行短暂的休眠,直接使用Thread.sleep()方法即可。
class MyThread implements Runnable //实现Runnable接口
{
public void run(){ //覆写run()方法
for(int i=0;i<50;i++){
try{
Thread.sleep(500); //线程休眠,间隔500毫秒
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 运行,i="+i); //取得当前线程的名称
}
}
}
public class ThreadSleepDemo01
{
public static void main(String args[]){
MyThread mt=new MyThread(); //实例化Runnable子类对象
//通过Thread类的构造方法启动线程,并手工设置线程名称
Thread t=new Thread(mt,"线程001"); //实例化Thread对象
t.start();
}
}

4.4、线程的中断
当一个线程运行时,另外一个线程可以直接通过interrupt()方法中断其运行状态。
class MyThread implements Runnable //实现Runnable接口
{
public void run(){ //覆写run()方法
System.out.println("1.进入run()方法!");
try{
Thread.sleep(10000); //线程休眠,间隔10秒
System.out.println("2.完成休眠!");
}catch(InterruptedException e){
System.out.println("3.线程休眠被终止!");
return ; //返回调用处
}
System.out.println("4.run()方法正常结束!");
}
}
public class ThreadInterruptDemo01
{
public static void main(String args[]){
MyThread mt=new MyThread(); //实例化Runnable子类对象
//通过Thread类的构造方法启动线程,并手工设置线程名称
Thread t=new Thread(mt,"线程001"); //实例化Thread对象
t.start();
try{
Thread.sleep(2000); //main线程休眠,间隔2秒
}catch(InterruptedException e){
}
t.interrupt();
}
}

4.5、设置后台线程
在Java程序中,只要前台有一个线程在运行,则整个Java进程都不会消失。所以此时可以设置一个后台线程,这样即使Java进程结束了,此后台线程依然会继续执行,要想实现这样的操作,可以使用setDaemon(boolean on);方法即可,参数on为true表示为后台运行。
class MyThread implements Runnable //实现Runnable接口
{
public void run(){ //覆写run()方法
while(true){
System.out.println(Thread.currentThread().getName()+"在运行!");
}
}
}
public class ThreadDaemonDemo01
{
public static void main(String args[]){
MyThread mt=new MyThread(); //实例化Runnable子类对象
//通过Thread类的构造方法启动线程,并手工设置线程名称
Thread t=new Thread(mt,"线程001"); //实例化Thread对象
t.setDaemon(true); //设置线程在后台运行
t.start();
}
}

4.6、线程的优先级
在Java线程操作中,所有的线程在运行前都会保持在就绪状态,那么此时,那个线程的优先级高,那个线程就有可能会先被执行。通过setPriority()方法设置线程的优先级。
class MyThread implements Runnable //实现Runnable接口
{
public void run(){ //覆写run()方法
for(int i=0;i<10;i++){
try{
Thread.sleep(500); //线程休眠,间隔500毫秒
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 在运行!"); //取得当前线程的名称
}
}
}
public class ThreadPriorityDemo01
{
public static void main(String args[]){
Thread t1=new Thread(new MyThread(),"线程001");
Thread t2=new Thread(new MyThread(),"线程002");
Thread t3=new Thread(new MyThread(),"线程003"); //实例化线程对象,并设置线程名称
t1.setPriority(Thread.MIN_PRIORITY); //设置优先级,最低
t2.setPriority(Thread.MAX_PRIORITY); //设置优先级,最高
t3.setPriority(Thread.NORM_PRIORITY); //设置优先级,中等
t1.start(); //启动线程
t2.start(); //启动线程
t3.start(); //启动线程
}
}

还可以通过getPriority()方法获得当前线程的优先级。
public class ThreadPriorityDemo02
{
public static void main(String args[]){
System.out.println("主方法的优先级:"+Thread.currentThread().getPriority()); //取得主方法的优先级
System.out.println("MIN_PRIORITY  = "+Thread.MIN_PRIORITY );
System.out.println("NORM_PRIORITY = "+Thread.NORM_PRIORITY);
System.out.println("MAX_PRIORITY  = "+Thread.MAX_PRIORITY );
}
}

4.7、线程的礼让
在线程操作中,也可以使用yieID()方法将一个线程的操作暂时让给其他线程执行。
class MyThread implements Runnable //实现Runnable接口
{
public void run(){ //覆写run()方法
for(int i=0;i<5;i++){
try{
Thread.sleep(500); //线程休眠,间隔500毫秒
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 在运行!"+i); //取得当前线程的名称
if(i==2){
System.out.println("当前线程:"+Thread.currentThread().getName()+"礼让。");
Thread.currentThread().yield(); //线程礼让
}
}
}
}
public class ThreadYieidDemo01
{
public static void main(String args[]){
Thread t1=new Thread(new MyThread(),"线程001");
Thread t2=new Thread(new MyThread(),"线程002"); //实例化线程对象,并设置线程名称
t1.start(); //启动线程
t2.start(); //启动线程
}
}

5、线程操作范例
设计一个线程操作类,要求可以产生三个线程对象,并可以分别设置三个线程的休眠时间,如下所示:
1. 线程001,休眠10秒
2. 线程002,休眠20秒
3. 线程003,休眠30秒

5.1、采用继承Thread类的方式
在类中应该存在保持线程名称和休眠时间的两个属性。但是Thread类中已经存在了name属性。
class MyThread extends Thread
{
private int time;
public MyThread(String name,int time){
super(name); //设置线程名称
this.time=time; //设置休眠时间
}
public void run(){
try{
Thread.sleep(this.time); //指定休眠时间
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"的休眠时间是:"+this.time+"毫秒!");
}
}
public class ThreadExampleDemo01
{
public static void main(String args[]){
MyThread mt1=new MyThread("线程001",10000); //实例化线程对象,并指定线程名称和休眠时间
MyThread mt2=new MyThread("线程001",20000); //实例化线程对象,并指定线程名称和休眠时间
MyThread mt3=new MyThread("线程001",30000); //实例化线程对象,并指定线程名称和休眠时间
mt1.start(); //启动线程
mt2.start();
mt3.start();
}
}

5.2、采用实现Runnable接口的方式
使用实现Runnable接口的方式,则类中没有线程名称的存在,所以要单独建立一个name属性,以保存线程的名称。
class MyThread implements Runnable
{
private int time;
private String name;
public MyThread(String name,int time){
this.name=name; //设置线程名称
this.time=time; //设置休眠时间
}
public void run(){
try{
Thread.sleep(this.time); //指定休眠时间
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(this.name+"的休眠时间是:"+this.time+"毫秒!");
}
}
public class ThreadExampleDemo02
{
public static void main(String args[]){
new Thread(new MyThread("线程001",5000)).start();
new Thread(new MyThread("线程002",10000)).start();
new Thread(new MyThread("线程003",15000)).start();
}
}


6、同步与死锁
在多线程的开发中,同步与死锁的概念非常重要,掌握以下重点:
1. 哪来需要同步
2. 如何实现同步,代码了解即可
3. 以及实现同步后会有哪些副作用,了解
概念必须清楚。

6.1、问题引出
以卖火车票为例,我们买票可以在火车站或各个售票点买,但是不管有几个售票点,最终一趟列车的 车票数量是固定的。如果把售票点理解成线程的话,则所有的线程应该是共同拥有一份固定数量的票。
class MyThread implements Runnable
{
private int ticket=5; //假设一共有5张票
private String name;
public void run(){
for(int i=0;i<100;i++){
if(ticket>0){ //还有票
try{
Thread.sleep(300); //加入延迟,指定休眠时间(模拟网络延迟)
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票,ticket="+ticket-- );
}
}
}
}
public class ThreadSyncDemo01
{
public static void main(String args[]){
MyThread mt=new MyThread();
new Thread(mt,"线程001").start();
new Thread(mt,"线程002").start();
new Thread(mt,"线程003").start();
}
}

执行上面的代码可以发现,卖出的票数会成负数,即多卖了票,可见程序出了问题。
程序出现的问题:
从运行的结果可以发现,程序中加入了延迟操作,所以在运行的最后出现了负数的情况,那么为什么现在会这样的问题呢?从上面的操作代码可以发现对于票数的操作步骤如下:
1. 判断票数是否大于0,大于0则表示还有票可以卖
2. 如果票数大于0,则卖票出去
但是,在上面的操作代码中,第一步和第二步之间加入了延迟操作,那么一个线程有可能还没有对票数进行减操作之前,其他的线程就已经将票数减少了,这样一来就会出现票数为负的情况。
问题的解决:
如果想解决这样的问题,就必须使用同步,所谓的同步就是指多个操作在同一个时间段内只能有一个线程进行,其他线程要等待此线程完成之后才可以继续执行。
解决资源共享的同步操作问题,可以使用同步代码块及同步方法两种方式。

6.2、同步代码块
回顾,代码块分为四种:
普通代码块:是直接定义在方法之中的
构造块:是直接定义在类中的,优先于构造方法执行,重复调用
静态块:是使用static关键字声明的,优先于构造块执行,只执行一次
同步代码块:使用synchronized关键字声明的代码块,称为同步代码块

在代码块上加上“synchronized”关键字的话,则此代码块就称为同步代码块,格式如下:
Synchronized(同步对象){
需要同步的代码;
}
同步的时候必须指明同步的对象,一般情况下会将当前对象作为同步的对象,使用this关键字表示。
class MyThread implements Runnable
{
private int ticket=5; //假设一共有5张票
private String name;
public void run(){
for(int i=0;i<100;i++){
synchronized(this){ //对当前对象进行同步
if(ticket>0){ //还有票
try{
Thread.sleep(300); //加入延迟,指定休眠时间(模拟网络延迟)
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票,ticket="+ticket-- );
}
}
}
}
}
public class ThreadSyncDemo02
{
public static void main(String args[]){
MyThread mt=new MyThread();
new Thread(mt,"线程001").start();
new Thread(mt,"线程002").start();
new Thread(mt,"线程003").start();
}
}

运行上面的代码可以发现,程序加入了同步操作,不会产生负数的情况。但是,程序的执行效率明显降低。

6.3、同步方法
除了可以将需要的代码设置成同步代码块之外,也可以使用synchronized关键字将一个方法声明称同步方法,定义格式:
Synchronized 方法返回值 方法名称(参数列表){}
class MyThread implements Runnable
{
private int ticket=5; //假设一共有5张票
private String name;
public void run(){
for(int i=0;i<100;i++){ //调用同步方法
this.sale();
}
}
public synchronized void sale(){ //声明同步方法
if(ticket>0){ //还有票
try{
Thread.sleep(300); //加入延迟,指定休眠时间(模拟网络延迟)
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票,ticket="+ticket-- );
}
}
}
public class ThreadSyncDemo03
{
public static void main(String args[]){
MyThread mt=new MyThread();
new Thread(mt,"线程001").start();
new Thread(mt,"线程002").start();
new Thread(mt,"线程003").start();
}
}

6.4、死锁
1.资源共享时需要进行同步操作。
2.程序中过多的同步会产生死锁。

死锁一般情况下就是表示在互相等待,是在程序运行时出现的一种问题。
class ZhangSan //定义张三类
{
public void say(){
System.out.println("张三对李四说:你给我画,我再给你书!");
}
public void get(){
System.out.println("张三得到画了!");
}
}
class LiSi //定义李四类
{
public void say(){
System.out.println("李四对张三说:你给我书,我再给你画!");
}
public void get(){
System.out.println("李四得到书了!");
}
}
public class ThreadDeadLockDemo01 implements Runnable
{
private static ZhangSan zs=new ZhangSan(); //实例化static型对象
private static LiSi ls=new LiSi(); //实例化static型对象
private Boolean flag=false; //声明标志位,判断哪个先说话
public void run(){
if(flag){
synchronized(zs){ //同步张三
zs.say();
try{
Thread.sleep(300);
}catch(InterruptedException e){
e.printStackTrace();
}
synchronized(ls){
zs.get();
}
}
}else{
synchronized(ls){ //同步张三
ls.say();
try{
Thread.sleep(300);
}catch(InterruptedException e){
e.printStackTrace();
}
synchronized(zs){
ls.get();
}
}
}
}
public static void main(String args[]){
ThreadDeadLockDemo01 t1=new ThreadDeadLockDemo01(); //控制张三
ThreadDeadLockDemo01 t2=new ThreadDeadLockDemo01(); //控制李四
t1.flag=true;
t2.flag=false;
new Thread(t1).start();
new Thread(t2).start();
}
}

6.5、方法定义的完整格式
访问权限{public/default/protected/private}[final] [static] [synchronized] 返回值类型/void 方法名称(参数类型 参数名称,…) [throws Exception1,Exception2,…]{
    [return [返回值/返回调用处]];
}

7、线程操作案例——生产者和消费者
生产者和消费者问题:
生产者不断生产,消费者不断取走生产者生产的产品。生产者和消费者同时占有产品,那么可以定义一个类表示产品,通过这个类将生产者和消费者这两个线程联合到一起。
class Product //定义一个产品类
{
private String name; //定义产品的名称
private String content; //定义产品的作用

public void setName(String name){
this.name=name;
}
public String getName(){
return this.name;
}
public void setContent(String content){
this.content=content;
}
public String getContent(){
return this.content;
}
}
class Producer implements Runnable //定义生产者,实现Runnable接口完成多线程功能
{
private Product product;
public Producer(Product product){
this.product=product;
}
public void run(){
boolean flag=true; //定义标记位
for(int i=0;i<50;i++){ //生产50次产品
if(flag){
this.product.setName("Java");
try{
Thread.sleep(300); //加入延迟操作,更好的发现问题
}catch(InterruptedException e){
e.printStackTrace();
}
this.product.setContent("大型项目的编程语言!");
flag=false;
}else{
this.product.setName("JavaScript");
try{
Thread.sleep(300);
}catch(InterruptedException e){
e.printStackTrace();
}
this.product.setContent("脚本语言!");
flag=true;
}
}
}
}
class Consumer implements Runnable //定义消费者,实现Runnable接口完成多线程功能
{
private Product product;
public Consumer(Product product){
this.product=product;
}
public void run(){
boolean flag=false; //定义标记位
for(int i=0;i<50;i++){ //消费50次产品
try{
Thread.sleep(100); //加入延迟操作,更好的发现问题
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(this.product.getName()+"-->"+this.product.getContent());
}
}
}
public class ThreadCaseDemo01
{
public static void main(String args[]){
Product product=new Product(); //实例化产品对象
Producer pro=new Producer(product); //生产者
Consumer con=new Consumer(product); //消费者
new Thread(pro).start();
new Thread(con).start();
}
}
运行以上代码可以发现,产品的名称和作用不对应,这是因为加入了延迟操作,此时可以使用同步解决设置名称和作用的一致。
class Product //定义一个产品类
{
private String name; //定义产品的名称
private String content; //定义产品的作用

public void setName(String name){
this.name=name;
}
public String getName(){
return this.name;
}
public void setContent(String content){
this.content=content;
}
public String getContent(){
return this.content;
}
public synchronized void set(String name,String content){
this.setName(name);
try{
Thread.sleep(300); //加入延迟操作,更好的发现问题
}catch(InterruptedException e){
e.printStackTrace();
}
this.setContent(content);
}
public synchronized void get(){
try{
Thread.sleep(100); //加入延迟操作,更好的发现问题
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(this.getName()+"-->"+this.getContent());
}
}
class Producer implements Runnable //定义生产者,实现Runnable接口完成多线程功能
{
private Product product;
public Producer(Product product){
this.product=product;
}
public void run(){
boolean flag=true; //定义标记位
for(int i=0;i<50;i++){ //生产50次产品
if(flag){
this.product.set("java","都喜欢用它编写大型项目!");
flag=false;
}else{
this.product.set("JavaScript","脚本语言!");
flag=true;
}
}
}
}
class Consumer implements Runnable //定义消费者,实现Runnable接口完成多线程功能
{
private Product product;
public Consumer(Product product){
this.product=product;
}
public void run(){
boolean flag=false; //定义标记位
for(int i=0;i<50;i++){ //消费50次产品
this.product.get();
}
}
}
public class ThreadCaseDemo02
{
public static void main(String args[]){
Product product=new Product(); //实例化产品对象
Producer pro=new Producer(product); //生产者
Consumer con=new Consumer(product); //消费者
new Thread(pro).start();
new Thread(con).start();
}
}
运行以上代码,可以发现达到了名称和作用的一致,但是依然存在重复取的问题,并没有达到生产一个消费一个的功能要求。
要达到以上的功能要求,则可以增加一个标志位,假设标志位为boolean型变量,如果标志位的内容为true,则表示可以生产,但是不能取走,如果此时线程执行到了消费者线程则消费者应该等待,如果标志位的内容为false,则表示可以取走,但是不能生产,如果生产者线程此时在运行,则应该等待。
class Product //定义一个产品类
{
private String name; //定义产品的名称
private String content; //定义产品的作用
private boolean flag=false; //定义标志位

public void setName(String name){
this.name=name;
}
public String getName(){
return this.name;
}
public void setContent(String content){
this.content=content;
}
public String getContent(){
return this.content;
}
public synchronized void set(String name,String content){
if(flag){
try{
super.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
this.setName(name);
try{
Thread.sleep(300); //加入延迟操作,更好的发现问题
}catch(InterruptedException e){
e.printStackTrace();
}
this.setContent(content);
flag=true; //改变标志位
super.notify();
}
public synchronized void get(){
if(!flag){
try{
super.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
try{
Thread.sleep(100); //加入延迟操作,更好的发现问题
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(this.getName()+"-->"+this.getContent());
flag=false; //改变标志位
super.notify();
}
}
class Producer implements Runnable //定义生产者,实现Runnable接口完成多线程功能
{
private Product product;
public Producer(Product product){
this.product=product;
}
public void run(){
boolean flag=true; //定义标记位
for(int i=0;i<50;i++){ //生产50次产品
if(flag){
this.product.set("java","都喜欢用它编写大型项目!");
flag=false;
}else{
this.product.set("JavaScript","脚本语言!");
flag=true;
}
}
}
}
class Consumer implements Runnable //定义消费者,实现Runnable接口完成多线程功能
{
private Product product;
public Consumer(Product product){
this.product=product;
}
public void run(){
boolean flag=false; //定义标记位
for(int i=0;i<50;i++){ //消费50次产品
this.product.get();
}
}
}
public class ThreadCaseDemo03
{
public static void main(String args[]){
Product product=new Product(); //实例化产品对象
Producer pro=new Producer(product); //生产者
Consumer con=new Consumer(product); //消费者
new Thread(pro).start();
new Thread(con).start();
}
}
8、线程的生命周期
一个新的线程创建之后通过start()方法进入到运行状态,在运行状态中可以使用yield()方法进行礼让,但是仍然可以进行,如果一个线程需要暂停的话,可以使用suspend()、sleep()、wait(),如果现在线程不需要再执行,则可以通过stop结束(如果run()方法执行完毕也表示结束),或一个新的线程直接调用stop()方法也可以进行结束。
1. suspend():暂时挂起线程
2. resume():恢复挂起的线程
3. stop():停止线程
以上三个方法都会产生线程死锁的问题,所以不建议使用。如果要停止线程,可以通过设置标志位,让线程停止。
class MyThread implements Runnable
{
private boolean flag=true; //定义标志位
public void run(){
int i=0;
while(this.flag){
System.out.println(Thread.currentThread().getName()+"运行,i="+(i++));
}
}
public void stop(){
this.flag=false; //修改标志位
}
}
public class ThreadStopDemo01
{
public static void main(String args[]){
MyThread mt=new MyThread();
Thread t=new Thread(mt,"线程001");
t.start(); //启动线程
try{
Thread.sleep(20);
}catch(InterruptedException e){
e.printStackTrace();
}
mt.stop(); //修改标志位,停止运行
}
}


分享到:
评论

相关推荐

    线程 JAVA java线程 java线程第3版 java线程第2版第3版合集

    电子书相关:包含4个有关JAVA线程的电子书(几乎涵盖全部有关线程的书籍) OReilly.Java.Threads.3rd.Edition.Sep.2004.eBook-DDU Java Thread Programming (Sams) java线程第二版中英文 java线程第二版中英文 ...

    Java线程状态流转图

    Java线程状态流转图知识点总结 Java线程状态流转图是一种用于描述Java线程生命周期中不同的状态和状态转换的图形表示方式。该图形展示了Java线程从创建到终止的整个生命周期,并详细介绍了每种状态的特点和转换...

    java线程分析工具TDA

    Java线程分析是Java开发中的重要环节,尤其是在处理性能优化、死锁排查或者并发问题时。TDA(Thread Dump Analyzer)是一款强大的Java线程分析工具,它能够帮助开发者深入理解应用在运行时的线程状态,包括线程的...

    java 线程 dump 分析工具 2.3.3

    java 线程Dump 分析工具: Java的TDA线程转储分析器是一个用于分析Sun Java VM生成的线程转储和堆信息的小型Swing GUI(目前用1.4测试)。它从提供的日志文件中解析线程转储和类直方图。它提供关于发现的线程转储的...

    java线程.pdf

    根据提供的信息,我们可以推断出这份文档主要关注的是Java线程的相关内容。下面将围绕“Java线程”这一主题展开详细的介绍与解释。 ### Java线程基础 在Java语言中,线程是程序执行流的基本单元。一个标准的Java...

    Java线程详解大全

    Java线程是并发编程的核心部分,它允许程序在同一时间执行多个独立的任务,从而提高系统效率和响应速度。本文将深入探讨Java线程的概念、生命周期、实现方式以及相关的同步机制。 首先,理解线程的基本概念至关重要...

    java线程实例 各种小Demo

    Java线程是多任务编程的重要概念,它允许程序同时执行多个独立的任务,从而提高系统效率和响应速度。在Java中,线程可以分为用户线程和守护线程,前者是程序运行的基础,而后者是在所有用户线程结束时才终止的后台...

    Java线程使用教程

    Java线程是Java编程语言中的一个核心概念,它允许程序同时执行多个任务,极大地提高了程序的并发性和效率。本教程将深入探讨Java线程的使用,帮助开发者掌握这一关键技术。 一、线程基础 1. **线程的概念**:线程...

    Java线程.ppt

    Java线程是Java编程中的重要概念,特别是在多核处理器和并发处理中不可或缺。Java线程允许程序在同一时间执行多个不同的任务,从而提高了程序的效率和响应性。在燕山大学信息学院计算机系的课程中,李峰教授讲解了...

    java线程深入解析

    Java线程是Java编程语言中的核心概念,尤其在多任务处理和并发编程中扮演着重要角色。线程允许一个程序内部同时执行多个独立的控制流,使得程序能够更高效地利用处理器资源。本文将深入解析Java线程的相关知识点,...

    Java线程(第三版)

    《Java线程(第三版)》是一本深入探讨Java线程技术的专业书籍,旨在帮助开发者理解和掌握Java平台上的多线程编程。Java线程是并发编程的重要组成部分,它允许程序同时执行多个任务,从而充分利用系统资源,提高程序的...

    java线程入门级书籍

    ### Java线程入门知识点详解 #### 一、Java线程基础知识概述 **1.1 什么是线程?** 线程是程序执行流的最小单元,是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。在Java中...

    java线程文档大全

    Java线程是多任务编程中的核心概念,它允许程序同时执行多个不同的任务,极大地提高了程序的效率和响应性。在Java中,线程是通过Java.lang.Thread类或实现Runnable接口来创建和管理的。这份“java线程文档大全”包含...

    JAVA线程dump的分析

    JAVA线程dump的分析 JAVA线程dump是指在JAVA程序中,当前线程的状态和调用堆栈的快照,能够帮助开发者了解当前程序的执行情况,诊断问题和性能瓶颈。生成JAVA线程dump的方法在不同的操作系统下是不同的,在Windows...

    Java线程培训资料

    ### Java线程培训资料知识点详解 #### 一、Java线程基本概念 1. **如何编写与启动线程** - **方式一:继承Thread类** ```java class MyThread extends Thread { @Override public void run() { // 业务逻辑 ...

    Java-Thread-Affinity,将Java线程绑定到给定的内核.zip

    Java线程亲和性(Thread Affinity)是一个高级并发编程概念,主要涉及到操作系统调度和硬件资源的优化。在多核处理器系统中,线程亲和性允许开发者指定某个线程应该运行在哪个特定的处理器核心上,从而提高性能、...

    java 线程工具类 java 线程工具类

    java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具...

    4种常用Java线程锁的特点,性能比较、使用场景.pdf

    4种常用Java线程锁的特点,性能比较、使用场景 线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发...

    JAVA线程学习(源代码)

    本资源"JAVA线程学习(源代码)"提供了关于Java线程的源代码示例,帮助我们深入理解和实践线程的使用。 首先,我们要理解Java中的线程模型。Java线程由`java.lang.Thread`类或`java.util.concurrent.Executor`框架来...

    java线程状态转换图

    Java 线程状态转换图 Java 线程状态转换图是 Java 编程中非常重要的一个概念,它描述了线程在不同的状态之间的转换关系。了解线程状态转换图对 Java 编程的理解和应用非常重要。本文将详细介绍 Java 线程状态转换图...

Global site tag (gtag.js) - Google Analytics