015.java_多线程

进程:把一个程序在一个数据集上的一次执行称为一个进程。

线程:线程是进程中可独立执行的子任务。一个进程中可以有一个或多个线程。

进程与线程的区别:进程是资源分配单位,线程是调度和执行单位。每一个进程都有自己的主存空间,同一个进程中的线程共享这个主存空间,进程中的所有线程对主存空间都有存取权。

线程的创建和启动

单线程操作:

public class SingleThreadTest {
public static void method1(){
	System.out.println("method1");
}

public static void method2(){
	System.out.println("method2");
}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println("main方法开始执行");
		SingleThreadTest.method1();
		SingleThreadTest.method2();
		System.out.println("main方法执行结束");
	}

}

运行结果:

main方法开始执行
method1
method2
main方法执行结束

方法执行顺序:main()方法→method1()方法→method2()方法

运行main()方法的线程通常称为主线程。主线程都是由JVM来启动的

JVM允许应用程序执行时可以同时运行多个线程,通过java.lang.Thread类来实现。

每个线程对象通过run()方法运行,通过star()方法启动。

创建新的线程

两种方法:

1.继承Thread类并重写run()方法

2.实现Runnable接口的类(一般采用这种方法)

import java.net.URL;

public class SingleThreadTest {
public static void method1(){
	System.out.println("method1");
}

public static void method2(){
	System.out.println("method2");
}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
//		System.out.println("main方法开始执行");
//		SingleThreadTest.method1();
//		SingleThreadTest.method2();
//		System.out.println("main方法执行结束");
		System.out.println("main方法开始执行");
		Thread thread1=new MyThread();
		MyRunner mr=new MyRunner();
		Thread thread2=new Thread(mr);//把它的实例作为参数传入Thread类的构造方法
		//启动线程
		System.out.println("启动线程1");
		thread1.start();
		System.out.println("启动线程2");
		thread2.start();
		System.out.println(thread1.isAlive());
		URL url=Thread.currentThread()
				.getContextClassLoader()
				.getResource("");//使用方法链调用方式获取当前classpath的绝对路径URL表示法
		System.out.println(url);
		System.out.println("main方法执行结束");
	}

}

class MyThread extends Thread{//继承Thread类
	public void run(){
		//要在线程中执行的代码
		for(int i=0;i<100;i++){
			System.out.println("MyThread:"+i);
		}
	}
}

class MyRunner implements Runnable{//实现Runnable接口

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i=0;i<100;i++){
			System.out.println("MyRunner:"+i);
		}
		
	}
	
}

运行结果:

main方法开始执行
启动线程1
启动线程2
true
MyThread:0
MyThread:1
MyThread:2
MyThread:3
MyRunner:0
MyThread:4
...
MyThread:99
file:/E:/JavaProject/ThreadProject/bin/
main方法执行结束

Thread类的常用方法

public void star()启动线程,执行后不是立刻启动,而是等待jvm调度
public static Thread currentThread()返回对当前正在执行的线程对象的引用
public ClassLoader getContextClassLoader()返回该线程的上下文ClassLoader
public final boolean isAlive()测试线程是否活着
public Thread.State getState()返回该线程的当前状态,true(可运行状态/等待状态)flase(新建状态/中止状态)
public final String getName()返回该线程的名字
public final void setName(String name)设置该线程的名字
public final void setDaemon(boolean on)将该线程标记为守护线程或用户线程
public final void setPriority(int newPriority)更改线程的优先级
public static void sleep(long milis)throws InterruptedException在指定的毫秒数内让正在执行的进程休眠
public void interrupt()中断线程
public final void join()throws InterruptedException等待该线程终止
public static void yield()暂停当前正在执行的线程对象

URL url=Thread.currentThread()
		.getContextClassLoader()
		.getResource("");//使用方法链调用方式获取当前classpath的绝对路径URL表示法
System.out.println(url);

运行结果:

file:/E:/JavaProject/ThreadProject/bin/

线程分为用户线程和守护线程

守护线程是一种在后台提供通用性支持的线程,在没有其它用户线程运行时,会自动退出。如java垃圾回收线程。

thread.setDaemon(ture)  //把用户线程thred变成守护线程

线程的状态及其转换

一个线程创建后,他总是处于六个生命周期的其中一个状态。JDK中用Thread.State枚举了六种状态:

NEW:新建状态

RUNNABLE:可运行状态  正在jvm中执行

BLOCKED:阻塞状态 受堵塞并等待某个监视器锁的进程

WAITING:等待状态 无限期等待另一个进程执行某个操作

TIMED_WAITING:超时等待状态 等待另一个进程执行指定时间的操作的进程

TERMINATED:中止状态 已退出的进程

多线程的调度和优先级

线程的调度:jvm中提供的线程调度器有抢占式模型和分时间片模型

线程的优先级:优先级分为10级。数字越大,优先级越高。MIN_PRIORITY(1)、MAX_PRIORITY(10)、NORMAL_PRIORITY(5),可以通过setPriority()方法设置进程优先级、getPriority()方法获取进程的优先级。

为了提高程序的移植性,不建议手动提高优先级。

public class ThreadPriorityTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Thread t0=new Thread(new A());
		Thread t1=new Thread(new A());
		System.out.println("进程t0的优先级:"+t0.getPriority());
		System.out.println("进程t1的名字:"+t1.getName());
		t0.setPriority(10);
		t0.start();
		t1.start();
	}

}
class A implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i=0;i<100;i++){
			System.out.println(Thread.currentThread().getName()+":"+i);
			//Thread.currentThread()返回当前进程对象的引用
		}
	}
	
}

运行结果:

进程t0的优先级:5
进程t1的名字:Thread-1
Thread-0:0
Thread-0:1
...
Thread-0:98
Thread-0:99
Thread-1:0
Thread-1:1
...
Thread-1:99

线程的控制

线程睡眠  

用于调整线程的执行顺序

public class ThreadSleepTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Thread t0=new Thread(new SleepRunner());
		Thread t1=new Thread(new NormalRunner());
		t0.setPriority(10);//最高优先级
		t0.start();
		t1.start();
	}

}
class SleepRunner implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		try{
			Thread.sleep(100);//睡100毫秒
		}catch(InterruptedException e){
			e.printStackTrace();//引发异常的所有方法调用信息
		}
		for(int i=0;i<100;i++){
			System.out.println("SleepRunner:"+i);
		}
	}
	
}
class NormalRunner implements Runnable{
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i=0;i<100;i++)
		System.out.println(Thread.currentThread().getName()+":"+i);
	}
}

运行结果:

Thread-1:0
Thread-1:1
...
Thread-1:99
SleepRunner:0
SleepRunner:1
...
SleepRunner:99

线程让步

public class ThreadYieldTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Thread t0=new Thread(new NormalRunner());
		Thread t1= new Thread(new YieldRunner());
		t0.start();
		t1.start();
	}

}
class NormalRunner implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i=0;i<100;i++)
		System.out.println(Thread.currentThread().getName()+":"+i);
	}
	
}
class YieldRunner implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i=0;i<100;i++){
			if(i%10==0){
				Thread.yield();//暂停 让步给其它进程
			}
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}
	
}

运行结果:

...
Thread-1:8
Thread-1:9
Thread-0:6
Thread-0:7
Thread-0:8
Thread-0:9
Thread-0:10
Thread-0:11
Thread-0:12
Thread-0:13
Thread-0:14
Thread-0:15
Thread-0:16
Thread-0:17
Thread-0:18
Thread-0:19
Thread-0:20
Thread-0:21
Thread-1:10
...

线程加入

当线程需要接力来完成某项人物时,join()方法可以可以使两个交叉运行的线程变成顺序执行。

public class ThreadJoinTste {

	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub
		Thread t=new Thread(new JoinRunner());
		t.start();
		for (int i=0;i<=50;i++){
			System.out.println("main:"+i);
			try{
				if(i==30){  //在这之前t和main交替运行,当i=30时、只有t进程运行,t运行结束后、main把未完成的运行
				t.join();
				}
			}catch(InterruptedException e){
				e.printStackTrace();
			}
			
		}
	}

}
class JoinRunner implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i=0;i<100;i++){
			System.out.println("JoinRunner:"+i);
		}
//		try{
//			Thread.sleep(10);//先让main线程先运行
//		}catch(InterruptedException e){
//			e.printStackTrace();
//		}
	}
	
}

多线程的同步

如果一个程序中定义多个线程,同时对程序中的变量进行访问,赋值修改等,那么这么线程会影响对方的运行。可以对访问进行同步解决。

使用关键词synchronized来同步方法(使用synchronized修饰方法)或同步代码块(synchronized(this){ 同步代码块 },这种办法效率较高)、或者使用对象锁将代码块锁住。

public class TrickOfficeTest {
	public static void main(String[] args){
	//五个卖票处  一共1000张票
		SellTrickRunner sell=new SellTrickRunner();
	Thread t1=new Thread(sell);
	t1.setName("售票处1");
	t1.start();
	Thread t2=new Thread(sell);
	t2.setName("售票处2");
	t2.start();
	Thread t3=new Thread(sell);
	t3.setName("售票处3");
	t3.start();
	Thread t4=new Thread(sell);
	t4.setName("售票处4");
	t4.start();
	Thread t5=new Thread(sell);
	t5.setName("售票处5");
	t5.start();
	}
}
class SellTrickRunner implements Runnable{
	private int trickNumber=0;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		boolean flag=true;
		while(flag){
			flag=sell();
		}
	}
public  boolean sell(){  //同步售票方法
	boolean flag=true;
	synchronized(this){//JVM会保证线程执行synchronized代码块时不会被其它线程打断
	if(trickNumber<1000){
		trickNumber=trickNumber+1;//两个线程同时对共享数据操作,就会出错。
		System.out.println(Thread.currentThread().getName()+"卖出第"+trickNumber+"张票。");
	}else{
		flag=false;
	}
	try{
		Thread.sleep(15);//睡眠5秒 增大出错几率
	}catch(InterruptedException e){
		e.printStackTrace();
	}
	return flag;
	}
	
	}	
}
private Lock lock=new ReentrantLock();//创建Lock实例
lock.lock();
	if(trickNumber<1000){
		trickNumber=trickNumber+1;//两个线程同时对共享数据操作,就会出错。
		System.out.println(Thread.currentThread().getName()+"卖出第"+trickNumber+"张票。");
	}else{
		flag=false;
	}
	lock.unlock();

运行结果:

售票处1卖出第1张票。
售票处2卖出第2张票。
售票处3卖出第3张票。
...
售票处4卖出第998张票。
售票处1卖出第999张票。
售票处3卖出第1000张票。

死锁

转自:https://www.cnblogs.com/mudao/p/5867107.html

/*创建两个字符串a和b,
 * 再创建两个线程A和B,
 * 让每个线程都用synchronized锁住字符串(A先锁a,再去锁b;B先锁b,再锁a),
 * 如果A锁住a,B锁住b,A就没办法锁住b,B也没办法锁住a,
 * 这时就陷入了死锁。*/
public class DeadLock {
    public static String obj1 = "obj1";
    public static String obj2 = "obj2";
    public static void main(String[] args){
        Thread a = new Thread(new Lock1());
        Thread b = new Thread(new Lock2());
        a.start();
        b.start();  //如果只让a运行,就可以无限循环锁住
    }    
}
class Lock1 implements Runnable{
    @Override
    public void run(){
        try{
            System.out.println("Lock1 running");
            while(true){
                synchronized(DeadLock.obj1){
                    System.out.println("Lock1 lock obj1");
                    Thread.sleep(3000);//获取obj1后先等一会儿,让Lock2有足够的时间锁住obj2
                    synchronized(DeadLock.obj2){
                        System.out.println("Lock1 lock obj2");//B把obj2锁住了  A不能锁住 所以不能运行
                    }
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}
class Lock2 implements Runnable{
    @Override
    public void run(){
        try{
            System.out.println("Lock2 running");
            while(true){
                synchronized(DeadLock.obj2){
                    System.out.println("Lock2 lock obj2");
                    Thread.sleep(3000);
                    synchronized(DeadLock.obj1){
                        System.out.println("Lock2 lock obj1");
                    }
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}
Lock1 running
Lock2 running
Lock1 lock obj1
Lock2 lock obj2

解决死锁问题:加大锁的颗粒度,把各个资源的操作代码放在一个同步块内
 

线程交互

java.lang.Object类提供了wait、notify、notifyAll方法用于同步机制,但只能在synchronized方法、synchronized代码块和Lock里面用,否则会报错。

wait方法调用、当前线程将被中断运行,并且放弃该对象的锁。

notify方法调用,会唤醒并运行在此对象等待池中的某个线程。

notifyAll方法会唤醒所有等待这个对象的线程,使之称为可运行的线程

/*生产者消费者问题*/
public class ProductTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Repertory repertory=new Repertory();
		new Thread(new Producer(repertory)).start();
		new Thread(new Consumer(repertory)).start();
	}

}
class Repertory{//仓库
	private int product=0;//默认有0个商品
	public synchronized void addProduct(){ //生产者把产品放入仓库
		if(this.product>=5){
			try{
				this.wait();//满了
			}catch(InterruptedException e){
				e.printStackTrace();
			}
		}else{
			product++;
			System.out.println("生产者生产第"+product+"件商品");
			this.notifyAll();//通知等候区的消费者取商品
		}
	}
	public synchronized void getProduct(){//消费者从仓库拿东西
		if((this.product)<=0){
			try{this.wait();//缺货
			}catch(InterruptedException e){
				e.printStackTrace();
			}
		}else{
			System.out.println("消费者取走了第"+product+"件商品");
			this.product--;
			this.notifyAll();
		}
	}
}

class Producer implements Runnable{
	private Repertory repertory;
	public Producer(Repertory repertory) {
		this.repertory=repertory;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("生产者开始生产商品");
		while(true){
			try{
				Thread.sleep((int)(Math.random()*10)*100);//Math.random()返回带正号的double值  [0.0,1.0)
			}catch(InterruptedException e){
				e.printStackTrace();
			}
			repertory.addProduct();
		}
	}
	
}

class Consumer implements Runnable{
	private Repertory repertory;
	public Consumer(Repertory repertory) {
		this.repertory=repertory;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("消费者开始取走商品");
		while(true){
			try{
				Thread.sleep((int)(Math.random()*10)*100);//Math.random()返回带正号的double值  [0.0,1.0)
			}catch(InterruptedException e){
				e.printStackTrace();
			}
			repertory.getProduct();
		}
	}
	
}

运行结果:

生产者开始生产商品
消费者开始取走商品
生产者生产第1件商品
消费者取走了第1件商品
生产者生产第1件商品
消费者取走了第1件商品
生产者生产第1件商品
消费者取走了第1件商品
生产者生产第1件商品
生产者生产第2件商品
消费者取走了第2件商品
生产者生产第2件商品
生产者生产第3件商品
生产者生产第4件商品
...

用Timer类调度任务

java.util.Timer类用于执行定时任务

java.util.TimerTask用于创建定时任务

创建定时任务:继承TimerTask类并重写run()方法,将任务内容写在方法体内

执行定时任务:Timer类常用方法:

public void schedule(TimerTask task,long delay,long period):重复地以固定的延迟时间去执行一个任务。

public void scheduleAtFixedRate(TimerTask task,long delay,long period):重复地以固定的频率去执行一个任务。

public void cancle();终止此计时器

import java.util.Timer;
import java.util.TimerTask;

public class TimerTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Timer timer=new Timer();//创建一个计时器
//		timer.schedule(new MyTask(), 0,1000);//立刻执行  每隔   1000毫秒 = 1s 执行一次
		timer.scheduleAtFixedRate(new MyTask(), 0, 1000);
		try{
			Thread.sleep(10000);//主线程睡眠10s
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		timer.cancel();
	}

}
class MyTask extends TimerTask{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("起床");
	}
	
}

运行结果:

起床
起床
起床
起床
起床
起床
起床
起床
起床
起床

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值