java基础之多线程

java基础之多线程

1. 多线程概述

1.1 线程与进程

  进程:进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且每个进程都具有一定独立功能。
  线程:线程是进程中的一个执行单元,来完成进程中的某个功能。负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
  简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。

1.2 程序运行原理

  • 分时调度
    所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
  • 抢占式调度
    优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度

2. 线程的创建和启动

2.1 Thread类

  继承Thread类创建线程

  • 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程要完成的任务。因此把run()方法称为线程执行体。
  • 创建Thread子类的实例,即创建线程对象。
  • 调用线程对象的start()方法来启动线程。
public class ThreadTest extends Thread{

	private int i;
	public void run() {
		for(i=0;i<50;i++) {
			//当线程继承Thread类的时候,直接调用this即可获取当前的进程
			//Thread类的getName()方法会返回线程的名字
			System.out.println(getName()+" "+i);
		}
	}
	public static void main(String[] args) {
		for(int i =0;i<50;i++) {
			System.out.println(Thread.currentThread().getName()+" "+i);
			if(i==10) {
				//创建第一个线程
				new ThreadTest().start();
				//创建第二个线程
				new ThreadTest().start();
			}
		}
	}
}

2.2 Runnable接口

  实现Runnable接口创建线程类

  • 定义Runnable接口的实现类,并重写run()方法。
  • 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread方法。
  • 调用线程的start()方法来启动对象。
public class RunnableTest implements Runnable {
	private int i;
	@Override
	public void run() {
		for(i=0;i<50;i++) {
			System.out.println(Thread.currentThread().getName()+" "+i);
		}
	}

	public static void main(String[] args) {
		for(int i =0;i<50;i++) {
			System.out.println(Thread.currentThread().getName()+" "+i);
			if(i==10) {
				RunnableTest rs = new RunnableTest();
				new Thread(rs).start();
				new Thread(rs).start();
			}
		}
	}
}

  实现Runnable的原理和好处

  • 程序设计遵循的原则:开闭原则,对修改关闭,对扩展开放,减少线程本身和任务之间的耦合性。
  • 实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。
  • 继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,又有线程任务。
  • 实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。

2.3 匿名内部类

  使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。

  • 方式1:创建线程对象时,直接重写Thread类中的run方法
new Thread() {
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(getName() + " " + i);
		}
	}
}.start();
  • 方式2:使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法
new Thread(new Runnable() {
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + " " + i);
		}
	}
}).start();

2.4 多线程常用方法

  • 获取名字
    通过getName()方法获取线程对象的名字
  • 设置名字
    • 通过构造函数可以传入String类型的名字
    new Thread("yyy") {
    	public void run() {
            //do something...
    	}
    }.start(); 
    
    • 通过setName(String)方法可以设置线程对象的名字
    new Thread() {
    	public void run() {
    	    this.setName("abc");
    		for(int i = 0; i < 100; i++) {
    			System.out.println(this.getName() + "....bb");
    		}
    	}
    }.start(); 
    
  • 获取当前线程对象
    通过Thread.currentThread(), 主线程也可以获取
  • 休眠线程
    Thread.sleep(毫秒,纳秒), 控制当前线程休眠若干毫秒
  • 守护线程
    setDaemon(), 设置一个线程为守护线程, 该线程不会单独执行, 当其他非守护线程都执行结束后, 自动退出
Thread t1 = new Thread() {
	public void run() {
		for(int i = 0; i < 50; i++) {
			System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
};
Thread t2 = new Thread() {
	public void run() {
		for(int i = 0; i < 2; i++) {
			System.out.println(getName() + "...bb");
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
};

t1.setDaemon(true);						//将t1设置为守护线程

t1.start();
t2.start();
//运行结果
Thread-1...bb
Thread-0...aaaaaaaaaaaaaaaaaaaaaa
Thread-0...aaaaaaaaaaaaaaaaaaaaaa
Thread-1...bb
Thread-0...aaaaaaaaaaaaaaaaaaaaaa

  • 加入线程
    • join(), 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续
    • join(int), 可以等待指定的毫秒之后继续
    final Thread t1 = new Thread() {
    	public void run() {
    		for(int i = 0; i < 100; i++) {
    			System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
    			
    		}
    	}
    };
    Thread t2 = new Thread() {
    	public void run() {
    		for(int i = 0; i < 100; i++) {
    			System.out.println(getName() + "...bb");
    			try {
    				t1.join();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		
    		}
    	}
    };
    t1.start();
    t2.start();
    

2.5 同步代码块和同步方法

  • 同步代码块
      当多线程并发,有多段代码同时执行时,我们希望某一段代码执行的过程中CPU不要切换到其他线程工作,这时就需要同步。使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块。多个同步代码块如果使用相同的锁对象,那么他们就是同步的。如果两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段代码。
public class ThreadT {

	public void print1() {
		synchronized (ThreadT.class) {
			System.out.print("1");
			System.out.print("2");
			System.out.print("3");
			System.out.print("
");
		}

	}

	public void print2() {
		synchronized (ThreadT.class) {
			System.out.print("a");
			System.out.print("b");
			System.out.print("c");
			System.out.print("
");
		}

	}

	public static void main(String[] args) {
		ThreadT tT = new ThreadT();
		Thread t1 = new Thread() {
			public void run() {
				for (int i = 0; i < 100; i++) {
					tT.print1();
				}
			}
		};
		Thread t2 = new Thread() {
			public void run() {
				for (int i = 0; i < 100; i++) {
					tT.print2();
				}
			}
		};
		t1.start();
		t2.start();
	}
}

  • 同步方法
      使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的。
public class ThreadT {

	public synchronized void print1() {
		System.out.print(Thread.currentThread().getName()+"----");
		System.out.print("1");
		System.out.print("2");
		System.out.print("3");
		System.out.print("
");
	}

	public synchronized void print2() {
		System.out.print(Thread.currentThread().getName()+"----");
		System.out.print("a");
		System.out.print("b");
		System.out.print("c");
		System.out.print("
");
	}

	public static void main(String[] args) {
		ThreadT tT = new ThreadT();
		Thread t1 = new Thread() {
			public void run() {
				for (int i = 0; i < 1000; i++) {
					tT.print1();
				}
			}
		};
		Thread t2 = new Thread() {
			public void run() {
				for (int i = 0; i < 1000; i++) {
					tT.print2();
				}
			}
		};
		t1.start();
		t2.start();
	}
}
//输出结果(一部分)
Thread-0----123
Thread-0----123
Thread-1----abc
Thread-1----abc
Thread-1----abc
Thread-0----123
Thread-0----123

2.6 线程间通信

  多个线程并发执行时,在默认情况下CPU是随机切换线程的。如果我们希望他们有规律的执行,就可以使用通信,例如每个线程执行一次打印。
  如果希望线程等待,就调用wait(),如果希望唤醒等待的线程,就调用notify();这两个方法必须在同步代码中执行,并且使用同步锁对象来调用。notifyAll()方法是唤醒所有线程,JDK5之前无法唤醒指定的一个线程,如果多个线程之间通信,需要使用notifyAll()通知所有线程,用while来反复判断条件。

public class ThreadT {
	
	private int flag = 1;

	public synchronized void print1() {
		try {
		    /*
			if (flag != 1) {    //if 语句是在那里等待就在那里起来
				this.wait();
			}*/
			while(flag != 1){
			    this.wait();    //while循环是循环判断,每次都会判断标记
			}
			System.out.print("1");
			System.out.print("2");
			System.out.print("3");
			System.out.print("
");
			flag = 2;
			//this.notify();
			this.notifyAll();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public synchronized void print2() {
		try {
			while(flag != 2){
			    this.wait();  
			}
			System.out.print("a");
			System.out.print("b");
			System.out.print("c");
			System.out.print("
");
			flag = 3;
			this.notifyAll();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	public synchronized void print3() {
		try {
			while(flag != 3){
			    this.wait();  
			}
			System.out.print("+");
			System.out.print("-");
			System.out.print("*");
			System.out.print("
");
			flag = 1;
			this.notifyAll();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		ThreadT tT = new ThreadT();
		Thread t1 = new Thread() {
			public void run() {
				for (int i = 0; i < 100; i++) {
					tT.print1();
				}
			}
		};
		Thread t2 = new Thread() {
			public void run() {
				for (int i = 0; i < 100; i++) {
					tT.print2();
				}
			}
		};
		Thread t3 = new Thread() {
			public void run() {
				for (int i = 0; i < 100; i++) {
					tT.print3();
				}
			}
		};
		t1.start();
		t2.start();
		t3.start();
	}
}
//输出结果(一部分)
......
abc
+-*
123
abc
+-*
123
......

2.7 互斥锁

  使用ReentrantLock类的lock()和unlock()方法进行同步。使用Condition的await()和signal()来暂停和唤醒线程

public class ThreadT {

	private int flag = 1;
	private ReentrantLock rlock = new ReentrantLock();
	private Condition c1 = rlock.newCondition();
	private Condition c2 = rlock.newCondition();
	private Condition c3 = rlock.newCondition();

	public void print1() {
		rlock.lock();   //获得锁
		try {
			while (flag != 1) {
				c1.await();  //使当前线程等待,直到发出信号或中断
			}
			System.out.print("1");
			System.out.print("2");
			System.out.print("3");
			System.out.print("
");
			flag = 2;
			c2.signal();    //唤醒等待线程。 
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			rlock.unlock();   //释放锁
		}
	}

	public void print2() {
		rlock.lock();
		try {
			while (flag != 2) {
				c2.await();
			}
			System.out.print("a");
			System.out.print("b");
			System.out.print("c");
			System.out.print("
");
			flag = 3;
			c3.signal();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			rlock.unlock();
		}
	}

	public void print3() {
		rlock.lock();
		try {
			while (flag != 3) {
				c3.await();
			}
			System.out.print("+");
			System.out.print("-");
			System.out.print("*");
			System.out.print("
");
			flag = 1;
			c1.signal();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			rlock.unlock();
		}
	}

	public static void main(String[] args) {
		ThreadT tT = new ThreadT();
		Thread t1 = new Thread() {
			public void run() {
				for (int i = 0; i < 100; i++) {
					tT.print1();
				}
			}
		};
		Thread t2 = new Thread() {
			public void run() {
				for (int i = 0; i < 100; i++) {
					tT.print2();
				}
			}
		};
		Thread t3 = new Thread() {
			public void run() {
				for (int i = 0; i < 100; i++) {
					tT.print3();
				}
			}
		};
		t1.start();
		t2.start();
		t3.start();
	}
}
原文地址:https://www.cnblogs.com/huizhipeng/p/10108331.html