介绍:Java提供了非常优秀的多线程支持,程序可以通过非常简单的方式来启动多线程。本章主要内容为:多线程的创建、启动、控制以及同步操作,并介绍JDK 5新增的线程创建方式。

一、线程的创建与使用

  1. 继承Thread类创建线程

  • 建立一个继承Thread的子类
  • 重写Thread类的run()--把所执行的操作写在该方法中
  • 创建一个子类对象
  • 通过该对象调用start()方法
 //1.1. 建立一个继承Thread的子类
class MyThread1 extends Thread{
@Override
//1.2. 重写Thread类的run()
public void run() {
for(int i=;i<;i++){
if(i%==){
System.out.println(getName() + ":" + i);//Thread.currentThread().getName()
}
}
}
} //1.3. 创建一个子类对象
MyThread1 t1 = new MyThread1();
t1.setName("线程1"); //1.4. 通过该对象调用start()方法
t1.start();//注意,一个线程对象不能同时两次start,需要重新创建一个对象来start

  2. 实现Runnable接口创建线程

  • 建立一个Runnable接口的实现类
  • 实现Runnable中的抽象方法run()
  • 创建实现类对象
  • 将此对象作为参数传递到Thread类的构造器中,创建一个Thread类的对象
  • 通过Thread类的对象调用start()方法
 //2.1 建立一个Runnable接口的实现类
class MyThread2 implements Runnable{
//2.2. 实现Runnable中的抽象方法run()
@Override
public void run() {
for(int i=0;i<100;i++){
if(i%2==0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
} //2.3 2.4 创建实现类对象,将此对象作为参数传递到Thread类的构造器中,创建一个Thread类的对象
Thread t2 = new Thread(new MyThread2());
t2.setName("线程2"); //2.5. 通过Thread类的对象调用start()方法
t2.start();

  开发中优先选择后者:既没有类的单继承性的限制,而且实现的方式更适合来处理多个线程有共享数据的情况。

  3. 实现Callable接口创建线程

  • 创建一个Callable接口的实现类
  • 实现call方法,将此线程需要执行的操作写在方法体中
  • 创建Callable接口实现类的对象
  • 将上面的实现类对象作为参数创建FutureTask的对象
  • 将上面的FutureTask的对象作为参数创建Thread的对象
  • 利用Thread的对象调用start方法
  • 通过 FutureTask的对象.get() 方法获取call方法的返回值
 //3.1 创建一个Callable接口的实现类
class NumThread implements Callable{
@Override
//3.2 实现call方法,将此线程需要执行的操作写在方法体中
public Object call() throws Exception {
int sum = 0;
for(int i=1;i<=100;i++){
if(i%2==0){
System.out.println(i);
sum += i;
}
}
return sum;
}
} //3.3 创建Callable接口实现类的对象
NumThread num = new NumThread();
//3.4 将上面的实现类对象作为参数创建FutureTask的对象
FutureTask futureTask = new FutureTask(num);
//3.5 将上面的FutureTask的对象作为参数创建Thread的对象
Thread t = new Thread(futureTask);
t.setName("线程3");
//3.6 利用Thread的对象调用start方法
t.start(); try {
//3.7 通过 FutureTask的对象.get() 方法获取call方法的返回值
Object sum = futureTask.get();//get方法的返回值即为NumThread.call的返回值
System.out.println("共计:" + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}

  相较于Runnable接口更强大、可以抛出异常、支持泛型,但是需要借助FutureTask类

  4. 使用线程池创建线程

  • 创建指定线程数量的线程池
  • 执行指定的线程的操作,需要提供一个实现Runnable获Callable接口的对象
  • 关闭连接池
 class MyThread2 implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
if(i%2==0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
} //4.1 创建指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);//创建确定个数的线程池
// 4.2 执行指定的线程的操作,需要提供一个实现Runnable获Callable接口的对象
service.execute(new MyThread2());//参数只能是Runnable
service.execute(new MyThread3());//参数只能是Runnable
//4.3 关闭连接池
service.shutdown();

   5. Thread类中常见的方法

  • void start(): 启动线程,并执行对象的run()方法
  • run(): 线程在被调度时执行的操作
  • String getName(): 返回线程的名称
  • void setName(String name):设置该线程名称
  • static Thread currentThread(): 返回当前线程。在Thread子类中就是this,通常用于主线程和Runnable实现类
  • static void yield(): 线程让步,暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程,若队列中没有同优先级的线程,忽略此方法
  • join() : 当某个程序执行流中调用其他线程的 join() 方法时,调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止,使得低优先级的线程也可以获得执行
  • static void sleep(long millis):(指定时间:毫秒)令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。(抛出InterruptedException异常)
  • stop(): 强制线程生命期结束,不推荐使用
  • boolean isAlive(): 返回boolean,判断线程是否还活着
//给线程命名--方式一:通过setName()
t1.setName("线程1"); //设置该线程名称 //给线程命名--方式二:通过构造器
ThreadMethodTest2 t2 = new ThreadMethodTest2("*线程2");

  6. 线程优先级

  • MAX_PRIORITY:10
  • MIN _PRIORITY:1
  • NORM_PRIORITY:5

  涉及的方法

  • getPriority() :返回线程优先值
  • setPriority(int newPriority) :改变线程的优先级

  说明

  • 线程创建时继承父线程的优先级
  • 低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用

二、线程的同步

  1. 同步代码块

synchronized (同步监视器){
// 需要被同步的代码;
}

  同步监视器:俗称锁。任何一个类的对象都可以充当锁,要求多个线程必须共用同一个锁

  • Runnable接口的方式的所可以使用this,因为它自始至终只用了一个对象
  • 继承Thread类的方式不能用this,可以使用 类.class 来充当

  2. 同步方法:synchronized放在方法声明中,表示整个方法为同步方法。同步方法仍然涉及同步监视器,但无需显示说明,例如:

public synchronized void show (String name){
....
}

  3. lock锁

//1. 实例化ReentrantLock
try{
//2. 调用lock
//需要被同步的代码
}finally{
//3. 调用unlock
}

  4. 案例

 /**
* 例子:创建三个窗口卖票,总票数为100张.使用实现Runnable接口的方式
* 存在线程的安全问题,采用 同步代码块方式 解决
* synchronized(同步监视器){
* //需要被同步的代码
* }
* > 同步监视器:俗称锁。任何一个类的对象都可以充当锁,
* 要求多个线程必须共用同一个锁。
* > Runnable接口的方式的所可以使用this,因为它自始至终只用了一个对象
* > 继承Thread类的方式不能用this,可以使用 类.class 来充当
* @author shkstart
* @create 2019-02-13 下午 4:47
*/
class Window1 implements Runnable{ private int ticket = 100; //共享数据
private Object obj = new Object(); @Override
public void run() {
while(true){
synchronized (obj){ //synchronized(this)
if(ticket > 0){
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
}else{
break;
}
}
}
}
} public class TicketSafeBR {
public static void main(String[] args) {
Window1 w = new Window1(); Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w); t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3"); t1.start();
t2.start();
t3.start();
} }

Runnable-同步代码块方式

 /**
* 例子:创建三个窗口卖票,总票数为100张.使用实现Runnable接口的方式
* 存在线程的安全问题,采用 同步方法方式 解决
*
* > 同步方法仍然涉及同步监视器,但无需显示说明
* > 非静态同步方法的同步监视器是:this
* > 静态同步方法的同步监视器是:类.class
*
*
* @author shkstart
* @create 2019-02-13 下午 4:47
*/
class Window2 implements Runnable{ private int ticket = 100; @Override
public void run() {
while(true){
show();
}
} private synchronized void show(){ //同步监视器为this
if(ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
}
}
} public class TicketSafeMR {
public static void main(String[] args) {
Window2 w = new Window2(); Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w); t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3"); t1.start();
t2.start();
t3.start();
} }

Runnable-同步方法方式

 /**
*
* 例子:创建三个窗口卖票,总票数为100张.使用继承Thread类的方式
*
* 存在线程的安全问题,采用 同步代码块方式 解决
* synchronized(同步监视器){
* //需要被同步的代码
* }
* > 同步监视器:俗称锁。任何一个类的对象都可以充当锁,
* 要求多个线程必须共用同一个锁。
* > Runnable接口的方式的所可以使用this,因为它自始至终只用了一个对象
* > 继承Thread类的方式不能用this,可以使用 类.class 来充当(类.class只会加载一次)(反射)
* @author shkstart
* @create 2019-02-13 下午 4:20
*/
class Window extends Thread{
private static int ticket = 100;
//要求多个线程必须共用同一个锁,因此使用了static,
private static Object obj = new Object();//一定要注意,这里与Runnable接口的方式有所不同
@Override
public void run() { while(true){
synchronized (obj){ //synchronized(Window.class)
if(ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + ":卖票,票号为:" + ticket);
ticket--;
}else{
break;
}
}
}
}
} public class TicketSafeBT {
public static void main(String[] args) {
Window t1 = new Window();
Window t2 = new Window();
Window t3 = new Window(); t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3"); t1.start();
t2.start();
t3.start(); }
}

Thread-同步代码块方式

 /**
*
* 例子:创建三个窗口卖票,总票数为100张.使用继承Thread类的方式
*
* 存在线程的安全问题,使用 同步方法方式 来解决
*
* > 同步方法仍然涉及同步监视器,但无需显示说明
* > 非静态同步方法的同步监视器是:this
* > 静态同步方法的同步监视器是:类.class
*
*
* @author shkstart
* @create 2019-02-13 下午 4:20
*/
class Window3 extends Thread{ private static int ticket = 100;
@Override
public void run() {
while(true){
show();
}
} private static synchronized void show(){ //同步监视器:Window3.class
//private static synchronized void show(){ //此种方法错误,同步监视器:t1、t2、t3(不唯一)
if(ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
}
}
} public class TicketSafeMT {
public static void main(String[] args) {
Window3 t1 = new Window3();
Window3 t2 = new Window3();
Window3 t3 = new Window3(); t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3"); t1.start();
t2.start();
t3.start(); }
}

Thread-同步方法方式

 public class LockTest {
public static void main(String[] args) {
Window4 w = new Window4(); Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w); t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3"); t1.start();
t2.start();
t3.start(); } } class Window4 implements Runnable{ private int ticket = 100; //1. 实例化ReentrantLock
private ReentrantLock lock = new ReentrantLock(); @Override
public void run() {
while (true){
try{
//2. 调用lock
lock.lock();
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":售票,票号为:" + ticket);
ticket--;
}else
break;
}finally {
//3. 调用unlock
lock.unlock();
}
}
}
}

Lock锁方式

三、线程的通信

  • wait():令当前线程挂起并放弃CPU、同步资源并等待,使别的线程可访问并修改共享资源,而当前线程排队等候其他线程调用notify()或notifyAll()方法唤醒,唤醒后等待重新获得对监视器的所有权后才能继续执行。
  • notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待
  • notifyAll ():唤醒正在排队等待资源的所有线程结束等待.
 /**
* 线程的通信:
* 两个线程交替打印1-100:
* > wait()
* > notify()/notifyAll()
* > 注意:wait和notify的调用者必须是同一个对象
* > wait和notify必须使用在同步代码块或者同步方法中
*
* @author marc
* @creat 2020-02-07 上午9:22
*/
public class MsgTest {
public static void main(String[] args) {
Number n1 = new Number(); Thread t1 = new Thread(n1);
Thread t2 = new Thread(n1); t1.setName("线程1");
t2.setName("线程2"); t1.start();
t2.start(); }
} class Number implements Runnable{
private int i = 1;
@Override
public void run() {
while (true){ synchronized (this) { notify(); //this.notify() if(i<=100){
System.out.println(Thread.currentThread().getName() + ":" + i);
i++; try {
wait(); //this.wait()
} catch (InterruptedException e) {
e.printStackTrace();
} }else
break;
} }
}
}

最新文章

  1. 使用静态函数impl模式做接口
  2. clear属性
  3. mvc中测试网络
  4. hadoop小试
  5. 关于Mesos和Kubernetes的区别
  6. Android-----工程文件目录介绍
  7. Ubuntu下的杀毒
  8. .net中SessionState相关配置
  9. Asset Store
  10. 理解运算符 || 和 &amp;&amp; 及方法
  11. Shiro--权限控制
  12. linux设备驱动归纳总结(五):1.在内核空间分配内存【转】
  13. javascript 中 typeof 的使用
  14. Codeforces Gym 100231B Intervals 线段树+二分+贪心
  15. c++程序开发利器
  16. EXT经验--查询EditorGridPanel的tbar的默认配置对象
  17. hdu 3927 Math Geek
  18. INSTALLING QUARTUS II V.13.1 64 BIT ON RHEL/CENTOS 6 64 BIT
  19. [原]命令模式在MVC框架中的应用
  20. this 指针

热门文章

  1. echarts修改X、 Y坐标轴字体的颜色
  2. jar包和war包的介绍和区别(转载)
  3. Oracle存储过程----存储过程执行简单的增删改查
  4. JS中使用lambda筛选list
  5. jenkins常用
  6. set,get,setter
  7. 并发队列 ConcurrentLinkedQueue 及 BlockingQueue 接口实现的四种队列
  8. 2019牛客多校第三场B Crazy Binary String 思维
  9. UPF set_port_attribute
  10. php preg正则表达式的组成部分