`
wuzq1223
  • 浏览: 23978 次
  • 性别: 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(); //修改标志位,停止运行
}
}


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics