java之_Thread(线程)

进程:
	一个程序开启的时候,会启动一个进程,在进程中会开启线程,如果只有一个线程,那么这个线程就称为主线程。如果进程停止了,那么会干掉线程再推出。
	在windows系统上,如果启动一个应用之后推出,但是你在管理器上还能看见这个应用的.exe还在,原因就是里面还有线程在执行任务,把这个应用的.exe干掉,那么就会先干掉里面的线程,再推出。

java程序中,我们执行程序的时候,会开启java 的JVM,即启动java JVM的时候会有一个进程:java.exe。在这个进程中至少启动一个线程去执行main函数。

线程:就是程序内部的一条执行线索,在程序中,线程的任务就是去执行任务,比如下载获取数据等

线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。

每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。每个线程都可以或不可以标记为一个守护程序。当某个线程中运行的代码创建一个新 Thread 对象时,该新线程的初始优先级被设定为创建线程的优先级,并且当且仅当创建线程是守护线程时,新线程才是守护程序。

当 Java 虚拟机启动时,通常都会有单个非守护线程(它通常会调用某个指定类的 main 方法)。Java 虚拟机会继续执行线程,直到下列任一情况出现时为止:

  • 调用了 Runtime 类的 exit 方法,并且安全管理器允许退出操作发生。
  • 非守护线程的所有线程都已停止运行,无论是通过从对 run 方法的调用中返回,还是通过抛出一个传播到 run 方法之外的异常。
线程创建的两种方式:
package com.enterise.test.thread;

public class CreateThread {

	public static void main(String[] args) {
		ThreadOne threadOne = new ThreadOne();
		threadOne.start();
		
		new Thread(new RunnableOne()).start();
	}
}
/**
 * 线程创建方式一:
 * 	继承Thread
 * @author Always
 *
 */
class ThreadOne extends Thread {

	@Override
	public void run() {
		super.run();
		while (true) {
			try {
				this.sleep(500);
				System.out.println("thread :" + this.getName());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

}


/**
 * Thread创建方式二:
 * 	实现Runnable
 * @author Always
 *
 */
class RunnableOne implements Runnable {

	@Override
	public void run() {
		while (true) {
			try {
				new Thread().sleep(500);
				System.out.println("thread :"
						+ Thread.currentThread().getName());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
继承Thread的线程:
当启动这个线程的时候,会调用这个线程的run方法,其实不然,当我们这个run方法是覆盖父类的run方法的时候:
/**
     * If this thread was constructed using a separate 
     * <code>Runnable</code> run object, then that 
     * <code>Runnable</code> object's <code>run</code> method is called; 
     * otherwise, this method does nothing and returns. 
     * <p>
     * Subclasses of <code>Thread</code> should override this method. 
     *
     * @see     #start()
     * @see     #stop()
     * @see     #Thread(ThreadGroup, Runnable, String)
     */
    public void run() {
	if (target != null) {
	    target.run();
	}
    }
----------------------------
/* What will be run. */
    private Runnable target;

----------------------------
也就是说当继承thread的run方法覆盖父类,那么会执行父类的run方法,在父类中是去判断target是否为空,这个target是runnable接口,如果这个target不为空,那么就会去执行runnable中的run方法,而不是去执行thread本身的run方法。实验:
package com.enterise.test.thread;

public class TestThread {
	
	public static void main(String[] args) {
		
		
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true){
					try {
						new Thread().sleep(500);
						System.out.println("--runnable->"+Thread.currentThread().getName());
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}){
			@Override
			public void run(){
//				super.run();
				while(true){
					try {
						new Thread().sleep(500);
						System.out.println("--thread.run->"+Thread.currentThread().getName());
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}.start();
	}
	
	
	
	
}
结果:
执行runnable中的run代码。
如果有
super.run();
执行thread中的run代码。
实现方式和继承方式的区别:
实现方式和继承方式的区别:
	实现:避免了单继承的局限性。
	继承:thread中的run。
	实现:runnable中的run。
线程的两个方法(run  start):

start() 
          使该线程开始执行;Java 虚拟机调用该线程的 run 方法。

run() 
          如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
package com.enterise.test.thread;

public class ThreadTwo {
	
	public static void main(String[] args) {
		
		MethodThread thread = new MethodThread();
		thread.start();
//		thread.run();
		
		System.out.println("------main-thread-------");
	}
	
	

}

class MethodThread extends Thread {
	private int count = 5;
	private boolean tag = true;

	public void run() {
		
		while(tag){
			try {
				Thread.sleep(500);
				if(count > 0){
					count --;
					System.out.println("--->"+Thread.currentThread().getName());
				}else {
					tag = false;
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
}
执行结果:
start:
	------main-thread-------
	--->Thread-0
	--->Thread-0
	--->Thread-0
	--->Thread-0
	--->Thread-0
run:
	--->main
	--->main
	--->main
	--->main
	--->main
	------main-thread-------
	
即:
	调用start方法是启动线程调用run方法。
	调用run方法是创建了线程,执行run方法,没有开启线程,也就是执行一个类调用里面的方法而已。
线程的几个状态:


start();启动线程执行run方法。
sleep();线程执行时,遇到sleep,者该线程执行睡眠,当睡眠时间达到的时候,会自动在执行下去。
wait();等待,使该线程等待着,进入线程池中。
notify();唤醒线程。(线程池:先进先出,唤醒先进去的线程)唤醒一个线程。
notifyAll();唤醒所有线程(线程池中的所有线程)
stop();停止线程。
run();方法执行结束。消亡。

运行状态:持有cpu的执行权。
临时状态(阻塞):没有cpu执行权(等待cpu的执行权)。
冻结状态:放弃了执行资格。(wait、sleep)
线程安全问题:
就是多线程在操作同一块代码时对共享数据进行操作,导致数据不合理的情况出现。


线程安全问题都是由全局变量及静态变量引起的。
即线程操作的是每个线程共享的数据。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;
若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。


即:引发线程出现安全的前提:
1.两个或者两个以上的线程。
2.操作的数据是几个线程所共享的。


run方法中:
		if(count > 0){
			count --;
			System.out.println("--->"+Thread.currentThread().getName());
		}else {
			tag = false;
		}
	count:是全局变量。
	当第一个线程进入判断的代码时,比如是count=1,在没有进行操作--之前,cpu的执行权转到了另外一个线程上,
	这个时候count还是1,进入判断操作--,此时count=0,然后打印,之后另外一个线程抢到cpu的执行权,执行--操作,打印的数据就成为了-1。这样导致数据的不合理性。


	问题的根据就是在操作数据的时候几个线程同步进行着,解决问题的办法就是在操作数据的时候只让一条线程执行,等执行完之后,另外一条线程再去执行数据的操作,这样就避免了数据操作的错乱。
package com.enterise.test.thread;

public class ThreadTwo {
	
	public static void main(String[] args) {
		
		Ticket ticket = new Ticket();
		//开启两条线程执行卖票
		new Thread(new SellTicketThread(ticket)).start();
		new Thread(new SellTicketThread(ticket)).start();
	}
	

}

class SellTicketThread implements Runnable {
	
	private Ticket ticket;
	public SellTicketThread(Ticket ticket){
		this.ticket = ticket;
	}

	@Override
	public void run() {
		while(ticket.isok){
			try {
				Thread.sleep(50);
				//卖票
				ticket.sellTicket();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
		}
		
	}
}

class Ticket {
	private int ticketNum = 100;
	public boolean isok = true;
	
	public void sellTicket(){
		if(ticketNum > 0){
			ticketNum--;
			System.out.println("ticiketNum-->"+ticketNum);
		}else {
			isok = false;
		}
	}
}
/**
 * 执行结果:
 * ticiketNum-->10
   ticiketNum-->9
   ticiketNum-->9
   ticiketNum-->8
   ticiketNum-->7
 * 出现了两个9 9
 * 
 */


同步代码块和同步函数:synchronized
在出现操作共享数据的代码块上加同步代码块:
synchronized (this) {
			if(ticketNum > 1){
				ticketNum--;
				System.out.println("ticiketNum-->"+ticketNum);
			}else {
				isok = false;
			}
		}


1线程进入同步代码块之前会判断这个锁是否是开的,如果是开的者进去,之后把锁关掉,然后执行数据,其他的线程抢到了cpu的执行权,到锁之前判断锁如果锁是关的者是进不去就在此等候,里面的线程操作数据完毕之后,会把锁打开,其他的线程按照之前的步骤同样执行。
this代表的是锁,只要是任意的对象就可以,如果锁不一致,那么数据还是错乱的原因就是每个线程有各自的锁。
同步函数:
public synchronized void sellTicket(){
			if(ticketNum > 1){
				ticketNum--;
				System.out.println("ticiketNum-->"+ticketNum);
			}else {
				isok = false;
			}
	}

当同步函数是静态的时候:
class Ticket {
	private static int ticketNum = 100;
	public  static boolean isok = true;
	
	public static synchronized void sellTicket(){
			if(ticketNum > 1){
				ticketNum--;
				System.out.println("ticiketNum-->"+ticketNum);
			}else {
				isok = false;
			}
	}
}

结果其实还是错乱的:原因是synchronized的锁是this,但是静态函数在jvm加载代码的时候就为这个函数开辟了空间,这个时候还没有这个对象也就是没有this这个概念。
所以static的同步函数的锁是类.class
class Ticket {
	private static int ticketNum = 100;
	public  static boolean isok = true;
	public static void sellTicket(){
		synchronized(Ticket.class){
			if(ticketNum > 1){
				ticketNum--;
				System.out.println("ticiketNum-->"+ticketNum);
			}else {
				isok = false;
			}
		}
	}
}


daixu----
 
 
 
 
 

                
  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值