java note 9 多线程

版权声明:本文为博主原创文章,转前请跟博主吱一声。 https://blog.csdn.net/Ga4ra/article/details/91358274


把之前那篇java多线程重新排版一下。

1. 进程和线程

单核计算机中,CPU在某一时间点只能做一件事,多进程不能同时进行,而是高速交替运行。也就是说不能并发。

多进程不能提高速度,而是提高cpu使用率。

进程与进程之间内存独立。

多线程也不能提高速度,而是提高应用程序使用率。比如,多个客户端访问一个服务器。

线程和线程共享堆内存和方法区内存,栈内存独立。

java命令启动JVM等于启动一个进程,该进程自动启动一个主线程来调用main。在此之前所有线程都是单线程的。

多线程栈

1.1 线程的生命周期

5个状态:新建,就绪,运行,阻塞,消亡。

线程生命周期

1.2 线程创建

定义线程类

public class ThreadTest01{
	public static void main(String[] args){
		Thread t = new Processor();
		t.start();
		for(int i=0;i<10;i++){
			System.out.println("main-->"+i);
		}
	}
}

class Processor extends Thread{
	public void run(){
		for(int i=0;i<10;i++){
			System.out.println("run-->"+i);
		}
		
	}
}

/*
main-->0
main-->1
main-->2
main-->3
main-->4
main-->5
main-->6
main-->7
main-->8
main-->9
run-->0
run-->1
run-->2
run-->3
run-->4
run-->5
run-->6
run-->7
run-->8
run-->9
*/

实现Runnable接口

实现java.lang.Runnable,实现run()

推荐这种方法,因为保留了类的继承。

public class ThreadTest02{
	public static void main(String[] args){
		Thread t = new Thread(new Processor());
		t.start();
		for(int i=0;i<10;i++){
			System.out.println("main-->"+i);
		}
	}
}

class Processor implements Runnable{
	public void run(){
		for(int i=0;i<10;i++){
			System.out.println("run-->"+i);
		}
		
	}
}
/*
main-->0
run-->0
main-->1
main-->2
main-->3
main-->4
main-->5
main-->6
run-->1
run-->2
run-->3
run-->4
run-->5
run-->6
run-->7
main-->7
main-->8
main-->9
run-->8
run-->9
*/
public class ThreadTest03{
	public static void main(String[] args){
		Thread t1 = new Thread(new Processor());
		t1.start();

		Thread t2 = Thread.currentThread();
		System.out.println(t2.getName());
		System.out.println(t2);
		
		Thread t3 = new Thread(new Processor());
		t3.setName("t3");
		t3.start();
	}
}

class Processor implements Runnable{
	public void run(){
		Thread t = Thread.currentThread();
		System.out.println(t.getName());
		System.out.println(t);
	}
}
/*
main
Thread-0
Thread[Thread-0,5,main]
Thread[main,5,main]
t3
Thread[t3,5,main]
*/

2. 优先级

public final void setPriority(int newPriority)

public class ThreadTest04{
	public static void main(String[] args){
		Thread t1 = new Thread(new Processor());
		System.out.println(t1.getPriority());
		t1.setPriority(Thread.MAX_PRIORITY);
		System.out.println(t1.getPriority());
		

		Thread t2 = new Thread(new Processor());
		System.out.println(t2.getPriority());
		t2.setPriority(Thread.MIN_PRIORITY);
		System.out.println(t2.getPriority());

		t1.start();
		t2.start();
	}
}

class Processor implements Runnable{
	public void run(){
		for(int i=0;i<10;i++){
			System.out.println(Thread.currentThread().getName()+"--->"+i);
		}
	}
}
/*
5
10
5
1
Thread-0--->0
Thread-0--->1
Thread-0--->2
Thread-0--->3
Thread-0--->4
Thread-0--->5
Thread-0--->6
Thread-0--->7
Thread-0--->8
Thread-0--->9
Thread-1--->0
Thread-1--->1
Thread-1--->2
Thread-1--->3
Thread-1--->4
Thread-1--->5
Thread-1--->6
Thread-1--->7
Thread-1--->8
Thread-1--->9
*/

3. sleep()

public static void sleep(long millis) throws InterruptedException

该方法为其它线程让出时间片,进入阻塞状态,睡够了就进入就绪状态。睡眠中若被中断,会抛出InterruptedException

interrupt()依靠异常处理机制可以打断睡眠。

下面的程序5秒后有输出。

public class SleepTest01{
	public static void main(String[] args){
		Thread t1 = new Thread(new Processor());
		t1.start();

		try{
			Thread.sleep(5000);
		}catch(InterruptedException e){
			e.printStackTrace();
		}

		//依靠异常处理机制
		t1.interrupt();
	}
}

class Processor implements Runnable{
	public void run(){
		
		try{
			Thread.sleep(10000000000L);
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		for(int i=0;i<10;i++){
			System.out.println(Thread.currentThread().getName());
		}
	}
}
public class SleepTest02{
	public static void main(String[] args){

		//Thread t1 = new Thread(new Processor());
		Processor p = new Processor();
		Thread t1 = new Thread(p);
		t1.start();

		try{
			Thread.sleep(5000);
		}catch(InterruptedException e){
			e.printStackTrace();
		}

		//t1.run = false;
		p.run = false;
	}
}

class Processor implements Runnable{
	boolean run = true;
	public void run(){
		for(int i=0;i<10;i++){
			if(run){
				try{
					Thread.sleep(1000);
				}catch(InterruptedException e){
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"--->"+i);
			}else{
				return ;
			}
			
		}
	}
}

java.lang.Object的其它相关方法:

  • wait(),线程等待
  • notify(),唤醒

3.1 yield()

public static void yield()

让位方法与睡眠类似,只是时间不固定,只能把时间片让给同优先级的线程。

public class YieldTest{
	public static void main(String[] args){
		Thread t1 = new Thread(new Processor());
		t1.start();

		for(int i=0;i<100;i++){
			System.out.println(Thread.currentThread().getName()+"--->"+i);
		}

	}
}

class Processor implements Runnable{
	public void run(){
		for(int i=0;i<100;i++){
			System.out.println(Thread.currentThread().getName()+"--->"+i);
			if(i%20==0){
				Thread.yield();
			}
			
		}
	}
}

3.2 交替输出

notifyAll():Wakes up all threads that are waiting on this object’s monitor.

管程 (英语:Monitors,也称为监视器) 是操作系统中一种重要的程序结构,结构内的多个子程序(对象或模块)形成的多个工作线程互斥访问共享资源。这些共享资源一般是硬件设备或一群变量。管程实现了在一个时间点,最多只有一个线程在执行管程的某个子程序。与那些通过修改数据结构实现互斥访问的并发程序设计相比,管程实现很大程度上简化了程序设计。 管程提供了一种机制,线程可以临时放弃互斥访问,等待某些条件得到满足后,重新获得执行权恢复它的互斥访问。

public class T{
	public static void main(String[] args){
		Num num = new Num(1);
		Thread t1 = new Thread(new PrintOdd(num));
		Thread t2 = new Thread(new PrintEven(num));
		t1.setName("t1");
		t2.setName("t2");

		t1.start();
		try{
			Thread.sleep(1000);
		}catch(Exception e){
			e.printStackTrace();
		}
		t2.start();

	} 
}

class Num{
	int count;
	Num(int count){
		this.count = count;
	}
	public synchronized void printOdd(){
		System.out.println(Thread.currentThread().getName()+"--->"+(count++));
		this.notifyAll();
		try{
			Thread.sleep(1000);
			this.wait();
		}catch(Exception e){
			e.printStackTrace();
		}
		
	}
	public synchronized void printEven(){
		System.out.println(Thread.currentThread().getName()+"--->"+(count++));
		this.notifyAll();
		try{
			Thread.sleep(1000);
			this.wait();
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}
class PrintOdd implements Runnable{
	Num num;
	PrintOdd(Num num){
		this.num = num;
	}
	public void run(){
		while(true){
			num.printOdd();
		}
	}
}
class PrintEven implements Runnable{
	Num num;
	PrintEven(Num num){
		this.num = num;
	}
	public void run(){
		while(true){
			num.printEven();
		}
	}
}

wait()、notify/notifyAll()在获得锁的synchronized代码块中执行;当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待状态。只有当notify()notifyAll()被执行,才会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,直到执行完synchronized代码块的代码或是中途遇到wait() ,再次释放锁。

4. join()

public final void join() throws InterruptedException

成员方法,两个栈变成一个栈,执行有先后顺序。

把下面代码注释去掉,前后对比一下。

public class JoinTest{
	public static void main(String[] args){
		Thread t1 = new Thread(new Processor());
		t1.start();

		// try{
		// 	t1.join();
		// }catch(InterruptedException e){
		// 	e.printStackTrace();
		// }
		
		for(int i=0;i<5;i++){
			System.out.println(Thread.currentThread().getName()+"--->"+i);
		}

	}
}

class Processor implements Runnable{
	public void run(){
		for(int i=0;i<10;i++){
			try{
				Thread.sleep(1000);
			}catch(InterruptedException e){
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"--->"+i);
		}
	}
}

5. 锁

5.1 同步异步

异步编程模型:线程之间互不等待。

同步编程模型:线程1等待线程2结束后才能执行。降低程序使用率,但保证数据安全。此时等同单线程。

什么时候用同步:

  • 多线程
  • 多线程共享数据
  • 共享数据有修改操作

可以通过sleep()来验证异步模型的不安全性。

public class LockTest01{
	public static void main(String[] args){
		Account act = new Account("account0001",2000);
		Thread t1 = new Thread(new Processor(act));
		Thread t2 = new Thread(new Processor(act));

		t1.start();
		t2.start();
		
	}
}

class Account{
	private String actNo;
	private double balance;
	
	public Account(){}
	public Account(String actNo,double balance){
		this.actNo = actNo;
		this.balance = balance;
	}

	public void setActno(String actNo){
		this.actNo = actNo;
	}
	public void setBalance(double balance){
		this.balance = balance;
	}

	public String getActno(){
		return actNo;
	}
	public double getBalance(){
		return balance;
	}

	public void withdraw(double money){
		double after = balance - money;
		try{
			Thread.sleep(1000);
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		this.setBalance(after);
	}
}

class Processor implements Runnable{

	Account act;
	Processor(Account act){
		this.act = act;
	}

	public void run(){
		act.withdraw(1000.0);
		System.out.println("withdraw 1000.0,balance: "+act.getBalance());
		
	}
}
/*
withdraw 1000.0,balance: 1000.0
withdraw 1000.0,balance: 1000.0
*/

5.2 两种同步方法

同步对象

这种方法更精确,推荐。

synchronized(共享对象){
	同步代码
}

线程遇到synchronized会找共享对象的对象锁,找到后进入同步语句块执行,执行结束后归还锁。没找到则等待其它线程归还锁。

public class LockTest02{
	public static void main(String[] args){
		Account act = new Account("account0001",2000);
		Thread t1 = new Thread(new Processor(act));
		Thread t2 = new Thread(new Processor(act));

		t1.start();
		t2.start();
		
	}
}

class Account{
	private String actNo;
	private double balance;
	
	public Account(){}
	public Account(String actNo,double balance){
		this.actNo = actNo;
		this.balance = balance;
	}

	public void setActno(String actNo){
		this.actNo = actNo;
	}
	public void setBalance(double balance){
		this.balance = balance;
	}

	public String getActno(){
		return actNo;
	}
	public double getBalance(){
		return balance;
	}

	public void withdraw(double money){

		synchronized(this){
			double after = balance - money;
			try{
				Thread.sleep(1000);
			}catch(InterruptedException e){
				e.printStackTrace();
			}
			this.setBalance(after);
			}
	}
}

class Processor implements Runnable{

	Account act;
	Processor(Account act){
		this.act = act;
	}

	public void run(){
		act.withdraw(1000.0);
		System.out.println("withdraw 1000.0,balance: "+act.getBalance());
		
	}
}
/*
withdraw 1000.0,balance: 1000.0
withdraw 1000.0,balance: 0.0
*/

同步方法

public synchronized void withdraw(){}

这种方法,线程仍然拿走共享对象的锁。

java.lang.StringBuffer等模块的很多方法都有synchronized,是线程安全的。

方法没有synchronized声明则不用等待锁,下面的代码,m2()不需要等待m1()释放锁。

public class LockTest03{
	public static void main(String[] args){
		MyClass mc = new MyClass();
		Thread t1 = new Thread(new Processor(mc));
		t1.setName("t1");
		Thread t2 = new Thread(new Processor(mc));
		t2.setName("t2");

		t1.start();
		
		//t1 execute first
		try{
			Thread.sleep(1000);
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		

		t2.start();
		
	}
}

class MyClass{
	public synchronized void m1(){
		try{
			Thread.sleep(5000);
		}catch(InterruptedException e){
			e.printStackTrace();
		}

		System.out.println("m1...");
		
	}

	//needn't wait for m1 to finish
	public void m2(){
		
		System.out.println("m2...");
		
	}
}

class Processor implements Runnable{

	MyClass mc;

	public Processor(MyClass mc){
		this.mc = mc;
	}

	public void run(){
		String name = Thread.currentThread().getName();
		if("t1".equals(name)){
			mc.m1();
		}else if("t2".equals(name)){
			mc.m2();
		}		
	}
}

5.3 类锁

类只有一个,所以类锁是类级别的。

synchronized添加到静态方法上,线程执行到此会找类锁。即使不共享对象,同步静态方法也要等待其它线程释放锁。

public class LockTest04{
	public static void main(String[] args){
		MyClass mc1 = new MyClass();
		MyClass mc2 = new MyClass();

		Thread t1 = new Thread(new Processor(mc1));
		t1.setName("t1");
		Thread t2 = new Thread(new Processor(mc2));
		t2.setName("t2");

		t1.start();
		
		//t1 execute first
		try{
			Thread.sleep(1000);
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		

		t2.start();
		
	}
}

class MyClass{
	public synchronized static void m1(){
		try{
			Thread.sleep(5000);
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		System.out.println("m1...");
	}

	//looking for the lock of class
	public synchronized static void m2(){
		System.out.println("m2...");
	}
}

class Processor implements Runnable{

	MyClass mc;

	public Processor(MyClass mc){
		this.mc = mc;
	}

	public void run(){
		String name = Thread.currentThread().getName();
		if("t1".equals(name)){
			mc.m1();
		}else if("t2".equals(name)){
			mc.m2();
		}		
	}
}

5.4 死锁

synchronized里面嵌套synchronized时容易发生。

public class DeadLock{
	public static void main(String[] args){
		Object o1 = new Object();
		Object o2 = new Object();

		Thread t1 = new Thread(new T1(o1,o2));
		Thread t2 = new Thread(new T2(o1,o2));


		t1.start();	
		t2.start();
		
	}
}


class T1 implements Runnable{

	Object o1;
	Object o2;


	public T1(Object o1,Object o2){
		this.o1 = o1;
		this.o2 = o2;
	}

	//lock o1,then lock o2
	public void run(){
		synchronized(o1){
			try{
				Thread.sleep(1000);
			}catch(InterruptedException e){
				e.printStackTrace();
			}
			synchronized(o2){

			}
		}	
	}
}

class T2 implements Runnable{

	Object o1;
	Object o2;


	public T2(Object o1,Object o2){
		this.o1 = o1;
		this.o2 = o2;
	}

	//lock o2,then lock o1
	public void run(){
		synchronized(o2){
			try{
				Thread.sleep(1000);
			}catch(InterruptedException e){
				e.printStackTrace();
			}
			synchronized(o1){

			}
		}	
	}
}

6. 守护线程

线程分两种:

  • 用户线程
  • 守护线程

所有用户线程都结束时,守护线程才会结束,比如GC。

所以java命令启动了jvm、一个主线程,和一个GC线程。

public final void setDaemon(boolean on)

public class DaemonTest{
	public static void main(String[] args){
		Thread t = new Thread(new T());

		t.setDaemon(true);
		t.start();

		for(int i=0;i<5;i++){
			System.out.println(Thread.currentThread().getName()+"-->"+i);
			try{
				Thread.sleep(1000);
			}catch(InterruptedException e){
				e.printStackTrace();
			}
		}
	}
}

class T implements Runnable{	
	public void run(){
		int i = 0;
		while(true){
			System.out.println(Thread.currentThread().getName()+"-->"+i);
			i++;
			try{
				Thread.sleep(1000);
			}catch(InterruptedException e){
				e.printStackTrace();
			}
		}
	}
}

这个程序,如果true改为false,主线程结束后,线程 t 仍然继续输出。

7. 定时器

java.util.Timer

public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)

java.util.TimerTask--public abstract void run()

import java.util.*;
import java.text.*;
import java.text.ParseException;

public class TimerTest{
	public static void main(String[] args){
		Timer t = new Timer();
		try{
			t.schedule(
				new LogTimeTask(),
				new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2018-10-20 10:59:00"),
				5*1000);
		}catch(ParseException e){
			e.printStackTrace();
		}
		
		System.out.println(1);
	}
}
class LogTimeTask extends TimerTask{
	public void run(){
		System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
	}
}

猜你喜欢

转载自blog.csdn.net/Ga4ra/article/details/91358274