欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

java基础学习—线程,java基础线程

来源: javaer 分享于  点击 19202 次 点评:114

java基础学习—线程,java基础线程



java线程

1、java线程的理解

线程就是程序执行代码块的过程:它会与各种状态,或者叫生命周期:创建、就绪(启动)、执行、阻塞、睡眠、结束(消亡),

上面这个顺序是不确定的

注意区分系统的进程与java的线程的区别,以及cpu时间片对java线程的影响

1.1 java虚拟机JVM是进程,在windows任务管理器中显示的java.exe 

1.2 一个普通java程序启动后,main方法所在的线程是主线程,这时java后台还有一个线程:负责垃圾回收,释放内存空间

1.3 多线程:即多个线程同时运行。

特点:并发执行,提高效率,消耗资源较大

1.4 java创建线程是通过调用底层系统的接口来实现的

1.5 在java中创建线程有两种方式:一种是继承Thread类,另外一种是实现Runable接口

1.6 通过继承Thread方法创建线程:

*继承Thread类

*创建此对象,并调用start()方法

package thread;

public class TestThreadCreat{
    public static void main(String[] args) {
        ThreadCreat  t1 = new ThreadCreat("threadCreat1");
        ThreadCreat  t2 = new ThreadCreat("threadCreat2");
        t1.start();
        t2.start();
    }

}

class ThreadCreat extends Thread{
    public ThreadCreat(String name){
        super(name);
    }
    
    public void run(){
        int a = 1;
        while(a<=100){
            System.out.println(this.getName()+"__"+a++);
        }
    }
}


1.7 通过实现Runable接口创建线程:

步骤:

*定义类实现Runnable接口

*覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中。

 *通过Thread类建立线程对象。

*将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。

    为什么要将Runnable接口的子类对象传递给Thread的构造函数。

    因为,自定义的run方法所属的对象是Runnable接口的子类对象。

    所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。

*调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。


package thread;

public class TestRunnableCreate {

    public static void main(String[] args) {
        RunnableCreate runnableCreate1 = new RunnableCreate();
        RunnableCreate runnableCreate2 = new RunnableCreate();
        new Thread(runnableCreate1).start();
        new Thread(runnableCreate1).start();
    }


}

class RunnableCreate implements Runnable {
  
    public void run(){
        int a = 1;
        while(a<=100){
            System.out.println(Thread.currentThread().getName()+"__"+a++);
        }
    }
}


2 多线程的安全问题:当多个线程同时对相同的资源进行操作时,就有可能导致访问的资源不准确,这种情况就称之为线程安全问题。

package thread;
/*
 * 不安全的多线程售票,可能出现负票
 */

public class TestThreadDanger {

    public static void main(String[] args) {
         Ticket t1 = new Ticket();
         new Thread(t1,"TicketSaleMan1").start();
         new Thread(t1,"TicketSaleMan2").start();
         new Thread(t1,"TicketSaleMan3").start();
         new Thread(t1,"TicketSaleMan4").start();
    }

}

class Ticket implements Runnable {
    private int tick = 100;
    public void run(){
        while(true){
            if(tick>0){
                try{Thread.sleep(20);}catch(Exception e){}
                System.out.println(Thread.currentThread().getName()+"__sale:"+tick--);
            }
            else{
                break;
            }
        }
    }
}


2.1 java中通过同步这个概念来解决线程安全问题

即,将多个线程共通操作的资源进行访问控制(锁/锁旗标),在某一个时刻只能一天线程进行操作。关键字是:syschronized

同步的前提:*两个及两个以上线程

    *多个线程的访问控制使用同一个锁

2.2 java中同步有 种实现方式:同步代码块,同步函数(静态同步函数和非静态同步函数)

2.2.1 同步代码块:

将多线程共通执行的代码块放到同步代码块中执行,这样就可以通过同步代码块的锁进行访问控制。

如果一个线程先持有同步代码块的锁,则在它释放这个锁之前,其它现在只能在外面等待

package thread;

/*
 *增加同步代码块后,这个程序是线程安全的
 */

public class TestThreadDanger {

    public static void main(String[] args) {
        Ticket t1 = new Ticket();
        new Thread(t1, "TicketSaleMan1").start();
        new Thread(t1, "TicketSaleMan2").start();
        new Thread(t1, "TicketSaleMan3").start();
        new Thread(t1, "TicketSaleMan4").start();
    }

}

class Ticket implements Runnable {

    private int tick = 100;
    Object obj = new Object();

    public void run() {
        while (true) {
            //同步代码块,将同步代码放入同步代码块中执行
            synchronized (obj) {// obj为自己创建的锁旗标
                if (tick > 0) {
                    try {
                        Thread.sleep(20);
                    }
                    catch (Exception e) {
                    }
                    System.out.println(Thread.currentThread().getName() + "__sale:" + tick--);
                }

                else {
                    break;
                }
            }
        }
    }
}

2.2.2 同步函数:

将多线程并发访问的代码封装到函数里面,然后在函数声明的时候加上synchronized,进行同步访问控制。

同步函数的锁是this,即本对象

package thread;

/*
 * 同步函数
 */

public class TestThreadDanger {

    public static void main(String[] args) {
        Ticket t1 = new Ticket();
        new Thread(t1, "TicketSaleMan1").start();
        new Thread(t1, "TicketSaleMan2").start();
        new Thread(t1, "TicketSaleMan3").start();
        new Thread(t1, "TicketSaleMan4").start();
    }

}

class Ticket implements Runnable {

    private int tick = 100;
    Object obj = new Object();
    boolean haveTicket = true;
    public void run() {
        while (true) {
            if (haveTicket) 
                saleTicket();
            else
                break;
        }
    }
    
    
    public synchronized void saleTicket(){
        if (tick > 0) {
            try {Thread.sleep(20);}catch (Exception e) {}
            System.out.println(Thread.currentThread().getName() + "__sale:" + tick--);
        }else{
            haveTicket = false;
        }
    }
    
    
}

2.2.3 静态同步函数

静态同步函数的锁是该类的字节码文件对象

class Ticket implements Runnable
{
	private static  int tick = 100;
	//Object obj = new Object();
	boolean flag = true;
	public  void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(Ticket.class)
				{
					if(tick>0)
					{
						try{Thread.sleep(10);}catch(Exception e){}
						System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
					}
				}
			}
		}
		else
			while(true)
				show();
	}
	public static synchronized void show()
	{
		if(tick>0)
		{
			try{Thread.sleep(10);}catch(Exception e){}
			System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
		}
	}
}


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

		Ticket t = new Ticket();

		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		t1.start();
		try{Thread.sleep(10);}catch(Exception e){}
		t.flag = false;
		t2.start();


	}
}

2.3 使用静态同步函数实现单例设计模式的懒汉式

/*
 * 懒汉式,或者延迟加载,当需要的时候才创建这个类
 * 懒汉式要注意的是线程安全问题
 */
class SingletonDelay{
    private SingletonDelay(){};
    private static SingletonDelay sin = null;
    public static SingletonDelay getInstence(){
        if(sin == null){//双重判断的目的是为了减少锁的比较次数,提高效率
            synchronized(SingletonDelay.class){//静态方法的锁必须用类的字节码对象
                if(sin==null){
                    sin = new SingletonDelay();
                }
            }
        }
        return sin;
    }
}

2.4 死锁

死锁,当多个线程之间并发执行,出现线程无限等待的现象称为死锁。

死锁产生的可能原因:

--A线程持有锁1,A线程现在需要锁2里面被控制的资源;B线程持有锁2,B线程现在需要锁1里面被控制的资源。因此线程A与B陷入相互需要对方资源,恶性循环中。

--嵌套锁:锁里面有锁

/*
死锁。
同步中嵌套同步。

*/

class Ticket implements Runnable
{
	private  int tick = 1000;
	Object obj = new Object();
	boolean flag = true;
	public  void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(obj)
				{
					show();
				}
			}
		}
		else
			while(true)
				show();
	}
	public synchronized void show()//this
	{
		synchronized(obj)
		{
			if(tick>0)
			{
				try{Thread.sleep(10);}catch(Exception e){}
				System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
			}
		}
	}
}


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

		Ticket t = new Ticket();

		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		t1.start();
		try{Thread.sleep(10);}catch(Exception e){}
		t.flag = false;
		t2.start();


	}
}


 2.5 多线程通信

多线程通信:等待/唤醒机制.即多个线程并发访问相同资源时,分工明确的线程,一个负责生产,一个负责消费,那么在这种情况下,

资源对于它们来时就是一个需要同步访问控制的东西。资源没有了,消费者就要通知生产者生产,消费者自己等待。生产者生产好

资源了,就应该通知消费者消费,自己等待。这就是多线程通信。

2.5.1 等待唤醒机制

在jdk1.5之前等待唤醒机制是通过在同步块里面使用方法:wait() :使线程进入冻结状态,,notify():唤醒线程池中排序的第一个线程,

notifyAll()唤醒线程池中所有等待的线程。

线程通信示例:

class ProducerConsumerDemo 
{
	public static void main(String[] args) 
	{
		Resource r = new Resource();

		Producer pro = new Producer(r);
		Consumer con = new Consumer(r);

		Thread t1 = new Thread(pro);
		Thread t2 = new Thread(pro);
		Thread t3 = new Thread(con);
		Thread t4 = new Thread(con);

		t1.start();
		t2.start();
		t3.start();
		t4.start();

	}
}

/*
对于多个生产者和消费者。
为什么要定义while判断标记。
原因:让被唤醒的线程再一次判断标记。


为什么定义notifyAll,
因为需要唤醒对方线程。
因为只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。

*/


class Resource
{
	private String name;
	private int count = 1;
	private boolean flag = false;
			//  t1    t2
	public synchronized void set(String name)
	{
		while(flag)
			try{this.wait();}catch(Exception e){}//t1(放弃资格)  t2(获取资格)
		this.name = name+"--"+count++;

		System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
		flag = true;
		this.notifyAll();
	}


	//  t3   t4  
	public synchronized void out()
	{
		while(!flag)
			try{wait();}catch(Exception e){}//t3(放弃资格) t4(放弃资格)
		System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
		flag = false;
		this.notifyAll();
	}
}

class Producer implements Runnable
{
	private Resource res;

	Producer(Resource res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			res.set("+商品+");
		}
	}
}

class Consumer implements Runnable
{
	private Resource res;

	Consumer(Resource res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			res.out();
		}
	}
}

2.5.2 JDK1.5对线程锁的升级

在JDK 1.5中,提供了改进synchronized的升级解决方案。将同步synchronized替换为显式的Lock操作,将Object中的wait,notify,notifyAll替换成Condition对象,该对象可对Lock锁进行获取。wait,notify,notifyAll这些方法都定义在Object中,是因为这些方法操作同步中的线程时,都必须表示自己所操作的线程的锁,就是说,等待和唤醒的必须是同一把锁。不可对不同锁中的线程进行唤醒。所以这就使得程序是不良的,因此,通过对锁机制的改良,使得程序得到优化。


/*
线程间通信:

等待唤醒机制:升级版

生产者消费者  多个

*/
import java.util.concurrent.locks.*;

class ProducerConsumerDemo{
	public static void main(String[] args){
		Resouse r = new Resouse();
		Producer p = new Producer(r);
		Consumer c = new Consumer(r);
		Thread t1 = new Thread(p);
		Thread t2 = new Thread(c);
		Thread t3 = new Thread(p);
		Thread t4 = new Thread(c);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

class Resouse{
	private String name;
	private int count = 1;
	private boolean flag =  false; 
	private Lock lock = new ReentrantLock();
	private Condition condition_P = lock.newCondition();
	private Condition condition_C = lock.newCondition();
//要唤醒全部,否则都可能处于冻结状态,那么程序就会停止。这和死锁有区别的。
	public void set(String name)throws InterruptedException{
		lock.lock();
		try{
			while(flag)//循环判断,防止都冻结状态
				condition_P.await();
			this.name = name + "--" + count++;
			System.out.println(Thread.currentThread().getName() + "..生成者--" + this.name);
			flag = true;
			condition_C.signal();
		}finally{
			lock.unlock();//释放锁的机制一定要执行
		}		
	}
	public void out()throws InterruptedException{
		lock.lock();
		try{
			while(!flag)//循环判断,防止都冻结状态
				condition_C.await();
			System.out.println(Thread.currentThread().getName() + "..消费者." + this.name);
			flag = false;
			condition_P.signal();//唤醒全部
		}finally{
			lock.unlock();
		}
	}
}

class Producer implements Runnable{
	private Resouse r;
	Producer(Resouse r){
		this.r = r;
	}
	public void run(){
		while(true){
			try{
				r.set("--商品--");
			}catch (InterruptedException e){}
		}
	}
}

class Consumer implements Runnable{
	private Resouse r;
	Consumer(Resouse r){
		this.r = r;
	}
	public void run(){
		while(true){
			try{
				r.out();
			}catch (InterruptedException e){}
		}
	}
}

2.6 停止线程

从JDK1.5自后线程里面的方法stop()显示已经过时,那么我们怎么停止线程呢?肯定是结束run()方法呀,怎么结束run()方法呢?一般情况下,run方法里面就有while循环,我们结束循环就可以了。

通过标记结束循环:

class StopThread implements Runnable{
	private boolean flag = true;
	public synchronized void run(){
		while (flag){
			try{
				wait();
			}catch (InterruptedException e)	{
				System.out.println(Thread.currentThread().getName() + "----Exception");
				flag = false;
			}
			System.out.println(Thread.currentThread().getName() + "----run");
		}
	}
	public void changeFlag(){
		flag = false;
	}
}

class  StopThreadDemo{
	public static void main(String[] args) {
		StopThread st = new StopThread();

		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);
		
		t1.start();
		t2.start();

		int n = 0;
		while (true){
			if (n++ == 60){
				st.changeFlag();
				break;
			}
			System.out.println("Hello World!");
		}
	}
}

上面有特殊情况:当线程处于冻结状态,就不会读取标记,那么线程就不会结束

因此我们必须使用Thread类里面提供的interrupt()方法,来中断线程,但不会让它死掉,会让线程脱了冻结状态,恢复到运行状态,再去判断标记,然后结束循环,最终跳出run方法。

class StopThread implements Runnable{
	private boolean flag = true;
	public synchronized void run(){
		while (flag){
			try{
				wait();
			}catch (InterruptedException e){
				System.out.println(Thread.currentThread().getName() + "----Exception");
				flag = false;
			}
			System.out.println(Thread.currentThread().getName() + "----run");
		}
	}
}

class  StopThreadDemo{
	public static void main(String[] args){
		StopThread st = new StopThread();
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);		
		t1.start();
		t2.start();
		int n = 0;
		while (true){
			if (n++ == 60){
				t1.interrupt();
				t2.interrupt();
				break;
			}
			System.out.println("Hello World!");
		}
	}
}

2.7 守护线程

在线程创建后,在启动以前,调用方法setDeamon(),可以将一个线程标记为守护线程。如果这个线程结束,则java虚拟机也会结束。


2.8  join线程

 当A线程执行到B线程方法时,A线程就会等待,B线程都执行完,A才会执行。join可用来临时加入线程执行。

class Demo implements Runnable{
	public void run(){
		for(int x=0;x<90;x++){
			System.out.println(Thread.currentThread().getName() + "----run" + x);
		}
	}
}

class  JoinDemo{
	public static void main(String[] args)throws Exception{
		Demo d = new Demo();
		Thread t1 = new Thread(d);
		Thread t2 = new Thread(d);
		t1.start();		
		t2.start();
		t1.join();//等t1执行完了,主线程才从冻结状态恢复,和t2抢执行权。t2执不执行完都无所谓。
		int n = 0;
		for(int x=0;x<80;x++){
			System.out.println(Thread.currentThread().getName() + "----main" + x);
		}
		System.out.println("Over");
	}
}

2.9 其它方法:

设置线程优先级:可通过setPriority()设定,如:setPriority(Thread.MAX_PRIORITY)设优先级为最大。

不执行这个线程,跳到其它线程:yield() 通过这个方法,可稍微减少线程执行频率,达到线程都有机会平均被执行的效果。




相关文章

    暂无相关文章
相关栏目:

用户点评