Java_多线程基础

自定义线程

实现方式

  1. 继承Thread类
  2. 实现Runnable 接口

继承Thread 类

  • 继承Thread后,要重写run 方法
  • public void run 方法体 为该线程要执行的任务
  • 启动线程的方法为start() ,不是调用run()
  • 调用run方法不能开启新的线程,只是普通的函数调用
  • 线程的生命周期类只能被启动一次
	public class xiancheng2 {

	public static void main(String[] args) {
			
		//创建对象
		Myrun  run1 =  new Myrun();
		//创建线程对象
		Thread  runTd = new Thread( run1 );
		// 开启线程
		runTd.start();
		run1.say();
	}
}

class Myrun extends Thread{
	@Override
	public void run() {
		System.out.println("新的线程");
	}
	
	public void say() {
		System.out.println( "美好的一天" );
	}
	
}

常用API

String getName() 返回该线程的名称。
long getId() 返回该线程的标识符。
int getPriority() 返回线程的优先级。
Thread.State getState() 返回该线程的状态。
join() 等待该线程终止。
join( long millis ) 等待该线程终止的时间最长为 millis 毫秒。
static void sleep(long millis ) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。
void start()
void interrupt() 中断线程。
static boolean interrupted() 测试当前线程是否已经中断。
boolean isDaemon() 测试该线程是否为守护线程。

实现Runnable 接口

  • 方法与继承Tread 类似即:

    • 继承Runnable 接口,实现run 方法
    • 创建线程对象 , 开启线程

差异

  1. 通过接口的方式,更加灵活(Java是单继承)
  2. 继承的方式更加简单。(使用较少)

线程状态

  1. 一个线程一般要经历5个状态
    • 新建状态
    • 准备状态
    • 运行状态
    • 等待/阻塞状态
      • 睡眠 : 调用sleep()
      • 阻塞 :
      • 挂起 : 调用suspend 挂起,resume()解除挂起 ( 不建议使用)
      • 等待 : 调用 wait()
    • 死亡状态

线程的使用

睡眠

  • 调用sleep方法可以让线程睡眠指定的时间后再苏醒进入准备状态,

    • public static void sleep( long millis) throw InterruptedException

    • public static void sleep( long millis , int nanos) throw InterruptedException

public class xiancheng2 {
	public static void main(String[] args) throws InterruptedException{
			
		//创建对象
		Myrun  run1 =  new Myrun();
		//创建线程对象
		Thread  runTd = new Thread( run1 );
		Thread  runTd2 = new Thread( run1 );
//		 开启线程
		runTd.start();
		//使主线程睡眠100ms
		Thread.sleep(100);	
		System.out.println( "美好的一天" );
	}
}

class Myrun implements Runnable{
	
	@Override
	public void run() {
		System.out.println("新的线程");
		try {
			// 使该线程睡眠100ms
			Thread.sleep(100);	
		} catch (Exception e) {
			// TODO: handle exception
		}
	}
}

线程的优先级

  • Java中线程的优先级用1~10之间的整数表示。值越大,优先级越高。默认为5

  • 不适合对线程进行细节调节

  • 主线程的优先级默认为5。子线程优先级初始值与父线程相同。

  • 改变优先级:(不能精确)

    • public final void setPriority( int newPriority )

public class xiancheng2 {
	public static void main(String[] args) {
			
		//创建对象
		Myrun  run1 =  new Myrun(1);
		Myrun  run2 =  new Myrun(2);
		//创建线程对象
		Thread  runTd1 = new Thread( run1 );
		Thread  runTd2 = new Thread( run2 );
		//设置优先级
		runTd2.setPriority( Thread.MIN_PRIORITY );//最高的优先级
		runTd1.setPriority( Thread.MAX_PRIORITY );//最低的优先级
		//开启线程
		
		runTd2.start();
		
		runTd1.start();
		
	}
}
class Myrun implements Runnable{
	private  int id ;
	public Myrun(int id) {
		this.id = id;
	}
	@Override
	public void run() {
		System.out.println("新的线程"+ this.id);
	}
	
}

线程的让步

  1. 分类

    • 线程只是让出当前的CPU资源,具体将CPU让给谁不能确定 (无保障)
    • 线程将给指定的线程让步,指定的线程没有完成,其绝不恢复执行 (线程合并--有保障)
  2. yield方法

    • 使当前正在运行的线程让出CPU,回到准备状态。(无保障)

      • pubic static void yield()

  3. join方法

  • 调用join方法的线程将执行完,后再恢复其他线程执行

    • public final void join( ) throw InterruptedException

    • public final void join( long millis) throw InterruptedException

    • public final void join( long millis ,int nanos ) throw InterruptedException

public class xiancheng2 {
	public static void main(String[] args) throws InterruptedException{
			
		Thread  run1 =  new Myrun("先");
		Thread  run2 =  new Myrun("后");
		
		run2.start();
		run2.join();	//run2线程将执行完后,在恢复其他线程。
		
		run1.start();
		for (int i = 0; i < 50; i++) {	
			System.out.print(6);
		}
	}
}
class Myrun extends Thread{
	private  String id ;
	public Myrun(String id) {
		this.id = id;
	}
	@Override
	public void run() {
		try {
			
		} catch (Exception e) {
			// TODO: handle exception
		}
		for(int  i=100 ;i>0;i--) {
			System.out.print(this.id);
		}
	}
}	

注意(有参)



如果A线程中掉用B线程的join(10),则表示A线程会等待B线程执行10毫秒,10毫秒过后,A、B线程并行执行。
需要注意的是,jdk规定,join(0)的意思不是A线程等待B线程0秒,而是A线程等待B线程无限时间,直到B线程执行完毕,即join(0)等价于join()。						
									                                        摘自:https://www.cnblogs.com/ldq2016/p/9045551.html

线程守护

  • 在后台运行的线程(内存管理,线程调度...) 称之为守护线程
  1. 开发守护线程

    • public final void setDaemon( boolean on ) // on表示是否设置为守护线程(fal se:普通线程)

      • java中 : 但前台线程(普通线程)都运行结束后,程序退出
        • 与回台程序是否运行完无关。

同步线程

同步方法

  • 同步方法是指用synchronized修饰的方法

    • synchronized <返回类型> 方法名 ([参数列表])[throw 异常]{

      }

      • 静态的同步方法的锁对象为 : 当前的class的对象
      • 非静态的同步方法的锁对象为 :this
      • 当线程获得锁锁后进入睡眠或让步,将带着锁一起睡眠,让步。
      • 同步方法退出后,锁将被释放,等待其他的线程可以获取锁。
      • 同步将会降低多线程的并发性。
public class tridet {

	public static void main(String[] args) {
		
		Ticket tck = new Ticket();
		Thread t2 = new Thread(tck,"window1");
		Thread t1 = new Thread(tck,"window2");
		Thread t3 = new Thread(tck,"window3");
		System.out.println( "tck: " + tck );
		t1.start();
		t2.start();
		t3.start();
	}
}

class Ticket implements Runnable{
	private  int  TicketNum = 100;
	
	public void run() {
		while( this.TicketNum>0 ) {
			this.buy();
		}
	}
	
	public synchronized void buy() {
		System.out.println( this );	 // this ==> tck(锁对象)
		if(this.TicketNum-->0) 
			System.out.println(Thread.currentThread().getName() + "买出一张票,还剩:" + this.TicketNum   );
	}
}

同步调度方法

final void wait() 使某个线程进入等待状态。直到别的线程调用notify或notifyAll方法将其唤醒
final void wait(long timeout) timeout 毫秒数,使某个线程进入等待状态,直至达到等待时间,或将其唤醒为止。
final void notify() 唤醒等待池中的某一个线程(没有保障,不能确定唤醒哪一个线程)
final void notifyAll() 唤醒等待池中的所有线程

同步语句块

synchronized (锁对象){ }

  • 锁对象要唯一
    • 锁对象就如同一个保管钥匙的人,要使保证同步那么就要保证只有当一个线程执行完了同步语句块后(归还钥匙)其他的线程才能进入同步语句块,所有保管钥匙的人要唯一(钥匙只有一把)。
public class tridet {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Thread t2 = new Ticket("window2");
		Thread t1 = new Ticket("window1");
		Thread t3 = new Ticket("window3");
		t1.start();
		t2.start();
		t3.start();
		
	}
}

class Ticket extends Thread{
	private static int  TicketNum = 10;
	private static Object  look = new Object();	// 锁对象(唯一)
	
	public void run() {
		while( this.TicketNum > 0 ) {
			synchronized (look) {
				if(this.TicketNum--<=0) break;
				System.out.println(getName() + "买出一张票,还剩:" + this.TicketNum   );
			}	
		}
	}
	
	public Ticket(String name) {
		super(name);
	}
}


public class tridet {

	public static void main(String[] args) {
		
		Ticket tck = new Ticket();
		
		Thread t2 = new Thread(tck,"window1");
		Thread t1 = new Thread(tck,"window2");
		Thread t3 = new Thread(tck,"window3");
		t1.start();
		t2.start();
		t3.start();
		
	}
}

class Ticket implements Runnable{
	private  int  TicketNum = 100;
    
	public void run() {
		while( this.TicketNum > 0 ) {
			synchronized (this) {	//锁对象为this , 即:tck对象(唯一)
				if(this.TicketNum--<=0) break;
				System.out.println(Thread.currentThread().getName() + "买出一张票,还剩:" + this.TicketNum   );
			}   
		}
	}
}

死锁

  • 相互等待对方释放资源的对象的 锁。

public class shisuo {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Object oj1 = new Object();
		Object oj2 = new Object();
		
		boy b1 = new boy( oj1 , oj2 );
		girl g1 = new girl( oj1 , oj2 );
		
		Thread thread1 = new Thread(b1,"boy");
		Thread thread2 = new Thread(g1,"boy");
		
		thread1.start();
		thread2.start();
	}
}

class boy implements Runnable {
	private Object look1, look2;
	public boy(Object look1, Object look2) {
		super();
		this.look1 = look1;
		this.look2 = look2;
	}
	public void run() {
		synchronized (look1) {
			System.out.println("body");
			synchronized (look2) {
				System.out.println("body2");
			}
		}
	}
}

class girl implements Runnable {
	private Object look1, look2;
	public girl(Object look1, Object look2) {
		super();
		this.look1 = look1;
		this.look2 = look2;
	}
	public void run() {
		synchronized (look2) {
			System.out.println("girl");
			synchronized (look1) {
				System.out.println("girl2");
			}
		}
	}
}

volatile 关键字

  • 作用:被其修饰的成员变量不允许一个线程对其操作过程中,其他线程插入操作。

  • 该关键字只允许修饰成员变量 , 不能修饰局部变量。

    • 局部变量不可能同时被两个线程
posted @ 2020-02-08 11:26  Tolbert  阅读(90)  评论(0)    收藏  举报