Java 多线程介绍及使用技巧

目录

 

一,简介

二,线程分类

三,多线程实现方式

四,线程常用方法

五,线程同步和线程安全

六,线程间通信


一,简介

1,什么是线程

     线程是程序执行的一条路径, 一个进程中可以包含多条线程

     多线程并发执行可以提高程序的效率, 可以同时完成多项工作

     有时称为轻量级进程,是CPU使用的基本单元;

2,线程组成

      由线程ID、程序计数器、寄存器集合和堆栈组成。

3,多线程的应用场景

     迅雷开启多条线程一起下载
     QQ同时和多个人一起视频
     服务器同时处理多个客户端请求

4,并发和并行的区别

      并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)
      并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。
      比如我跟两个网友聊天,左手操作一个手机跟甲聊,同时右手用另一台电脑跟乙聊天,这就叫并行。
      如果用一部手机先给甲发个消息,然后立刻再给乙发消息,然后再跟甲聊,再跟乙聊。这就叫并发。

二,线程分类

Java线程主要分为:用户线程(User Thread)和守护线程(Daemon Thread)

2.1,守护线程

该线程不会单独执行, 当其他非守护线程(用户线程)都执行结束后, 自动退出;

设置守护线程的方法:

//设置该线程为守护线程
thread.setDaemon(true);//注意必须在线程启动start()方法之前调用。

应用:垃圾回收线程

2.2,用户线程

主要包括:主线程和子线程(用户线程),子线程设置了setDaemon(true);就是守护线程;

注意:不能把主线程设置为守护线程,不然会报IllegalThreadStateException异常;

2.3,区别

用户线程的优先级高,守护线程的优先级低;

如果JVM中还存在用户线程,那么JVM就会一直存活,不会退出;但是JVM中只有守护线程的时候,JVM就会退出;

守护线程依赖于用户线程,JVM中所有用户线程退出了,所有的守护线程也就会退出 ;

总之,只要有用户线程存在,JVM就不会退出;所有用户线程都退出了,JVM也就退出了,JVM都退出了,守护线程也就退出了;

注意:以下代码的逻辑让守护线程提前于用户线程消亡的情况下,守护线程并不会主动延长生命和用户线程一起消亡。

public static void main(String[] args) {
	final Thread t = new Thread(){
		public void run() {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
			}
			System.out.println("守护线程");
		};
	};
		
	t.setDaemon(true);
	t.start();
		
	Thread t2 = new Thread(){
		public void run() {
		    for (int i = 0; i < 10; i++) {
			    System.out.println("用户线程   " + " 守护线程是否存活" +  t.isAlive());
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
				
		};
	};
	t2.start();
}

    
但是让守护线程延迟于用户线程消亡的情况下,守护线程会提前和用户线程一起消亡。

三,多线程实现方式

3.1,通过继承Thread类,开启一个线程,实现多线程;

public class ThreadDemo {
	public static void main(String[] args) {
		ThreadTest thTest = new ThreadTest();
		thTest.start();
		
		for (int i = 0; i < 1000; i++) {
			System.out.println("我是主线程");
		}
	}
}

class ThreadTest extends Thread{
	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		for (int i = 0; i < 1000; i++) {
			System.out.println("子线程");
		}
	}
}

以上通过打印信息可以看到,主线成和子线程是交替执行的,说明有多线程在运行;

3.2,通过实现Runnable接口

public class ThreadDemo2 {
	private void mian() {
		// TODO Auto-generated method stub
		MyRunnable r = new MyRunnable();
		
		Thread t= new Thread(r);
		t.start();
	}
}


class MyRunnable implements Runnable{

	public void run() {
		// TODO Auto-generated method stub
		for(int i=0;i<1000;i++){
			System.out.println("我是子线程");
		}
	}
	
}

3.3,匿名内部类实现,就是以上两种方式的另一种写法;

public class ThreadDemo3 {

	public static void main(String[] args) {
		new Thread(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				
				System.out.println("匿名内部类继承Thread,开启一个线程");
			}
		}.start();
		
		new Thread(new Runnable() {
			
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("匿名内部类实现Runnable接口,开启一个线程");
			}
		}).start();
	}
	
}

四,线程常用方法

4.1,获取线程的名字:getName();

public static void main(String[] args) {
	new Thread(){
	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		System.out.println(this.getName() + "   aaaaaa");
	}
    }.start();	
}

4.2,为线程设置名字,不设置有默认的名称:setName()

//第一种方式
public static void main(String[] args) {
new Thread(){
	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		this.setName("android ");
        System.out.println("当前线程名字:"+this.getName())
	}
}.start();

    //第二种方式,构造方法中为线程设置一个名字
 new Thread("Windows"){
	@Override
	public void run() {
	    // TODO Auto-generated method stub
		super.run();
        System.out.println("当前线程名字:"+this.getName())
	}
 }.start();
		
}

4.3,获取当前线程对象:Thread.currentThread

public static void main(String[] args) {
	new Thread(new Runnable() {
	    public void run() {
		    Thread.currentThread().setName("Window");
	    }
    }).start();

    //获取主线程的名字
    Thread.currentThread.getName();
}

4.4,让线程休眠的方法:Thread.sleep(休眠时间,单位毫秒);

4.5,join()线程加入方法:让当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续执行;

public static void main(String[] args) {
	final Thread t1 = new Thread(){
		public void run() {
			for (int i = 0; i < 10; i++) {
				System.out.println(this.getName()+ " aaaaaaa");
			}	
		};
	};
	Thread t2 = new Thread(){
		public void run() {
			for (int i = 0; i < 10; i++) {
				if(i==2){
					try {
						t1.join();//t1线程加入,t2等待t1执行完成再继续执行;
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				System.out.println(this.getName()+ " bbbbbbbbbbbbbbbbbb");
			}
		};
	};
	t1.start();
	t2.start();
}

打印输出:

Thread-0 aaaaaaa
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-0 aaaaaaa
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb

4.6,setPriority()设置线程优先级

优先级从0(Thread.MIN_PRIORITY)到10(Thread.MAX_PRIORITY)范围内;

默认优先级:Thread.NORM_PRIORITY=5

4.7,yield() 设置线程礼让,让出cpu,让其他线程执行;

五,线程同步和线程安全

https://blog.csdn.net/ezconn/article/details/100060812

六,线程间通信

6.1,什么时候需要通信

多个线程并发执行时, 在默认情况下CPU是随机切换线程的

如果我们希望他们有规律的执行, 就可以使用通信, 例如每个线程执行一次打印

6.2,两个线程间的通信

通过wait()和notify()方法实现,wait()方法让当前线程等待,notify()唤醒正在等待的线程;

例如:两个线程实现循环重复打印两句诗

实现:

public class ThreadDemo1 {
	
	public static void main(String[] args) {
		final Printer p = new Printer();
		
		new Thread(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				int i =0;
				while (i<1000) {
					i++;
					try {
						p.print1();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
	    }.start();
		new Thread(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				int i =0;
				while (i<1000) {
					i++;
					try {
						p.print2();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
		}.start();
		
	}

}

class Printer{
	int flag = 0;
	public synchronized void print1() throws InterruptedException{//同步方法默认的锁对象是this
		
		if(flag!=0){
			this.wait();//同步锁对象调用wait()
		}
		
		System.out.print("窗");
		System.out.print("前");
		System.out.print("明");
		System.out.print("月");
		System.out.print("光");
		System.out.print("\r\n");
		
		flag = 2;
		this.notify();//同步锁对象调用notify()
	}
	public synchronized void print2() throws InterruptedException{//同步方法默认的锁对象是this
		
		if(flag!=2){
			this.wait(); //同步锁对象调用wait()
		}
		
		System.out.print("疑");
		System.out.print("是");
		System.out.print("地");
		System.out.print("上");
		System.out.print("霜");
		System.out.print("\r\n");

		flag = 1;
		
		this.notify();//同步锁对象调用notify()
	}

}

注意:wait()和notify()这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用

6.3,三个或者三个以上的线程通信

通过wait()让当前线程等待,notifyAll()方法是唤醒所有线程;

public class ThreadDemo2 {
	
	public static void main(String[] args) {
		final Printer2 p = new Printer2();
		
		
		new Thread(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				int i =0;
				while (i<1000) {
					i++;
					try {
						p.print1();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
		}.start();
		new Thread(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				int i =0;
				while (i<1000) {
					i++;
					try {
						p.print2();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
		}.start();
		new Thread(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				int i =0;
				while (i<1000) {
					i++;
					try {
						p.print3();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
		}.start();
		new Thread(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				int i =0;
				while (i<1000) {
					i++;
					try {
						p.print4();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
		}.start();
	}

}

class Printer2{
	int flag = 0;
	public synchronized void print1() throws InterruptedException{
		
		while(flag!=0){
			this.wait();
		}
		
		System.out.print("窗");
		System.out.print("前");
		System.out.print("明");
		System.out.print("月");
		System.out.print("光");
		System.out.print("\r\n");
		
		flag = 1;
		this.notifyAll();  //
	}
	public synchronized void print2() throws InterruptedException{
		
		while(flag!=1){
			this.wait();
		}
		
		System.out.print("疑");
		System.out.print("是");
		System.out.print("地");
		System.out.print("上");
		System.out.print("霜");
		System.out.print("\r\n");
		
		flag = 2;
		
		this.notifyAll();
	}
	public synchronized void print3() throws InterruptedException{
		
		while(flag!=2){
			this.wait();
		}
		
		System.out.print("举");
		System.out.print("头");
		System.out.print("望");
		System.out.print("明");
		System.out.print("月");
		System.out.print("\r\n");
		
		flag = 3;
		
		this.notifyAll();
	}
	public synchronized void print4() throws InterruptedException{
		
		while(flag!=3){
			this.wait();
		}
		
		System.out.print("低");
		System.out.print("头");
		System.out.print("思");
		System.out.print("故");
		System.out.print("乡");
		System.out.print("\r\n");
		
		flag = 0;
		
		this.notifyAll();
	}

}

6.4,JDK1.5之前通过上面的方式,实现多个线程间通信,1.5版本增加了一个互斥锁ReentrantLock实现;

使用ReentrantLock类中的lock()和unLock()方法替代synchronized实现同步;然后再ReentrantLock创建Condition(监视器)子类对象,每个线程中放一个Condition;通过Condition的await()和signal() 方法实现当前线程等待和唤醒指定的线程;await()相当于Object中的wait()方法;signal()和Object中的notify()和notifyAll()方法有点不一样;

例如:


public class fThreadDemo3 {

	public static void main(String[] args) {
		final Printer3 p = new Printer3();

		new Thread() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				int i = 0;
				while (i < 1000) {
					i++;
					try {
						p.print1();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

			}
		}.start();
		new Thread() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				int i = 0;
				while (i < 1000) {
					i++;
					try {
						p.print2();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

			}
		}.start();
		new Thread() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				int i = 0;
				while (i < 1000) {
					i++;
					try {
						p.print3();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

			}
		}.start();
		new Thread() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				int i = 0;
				while (i < 1000) {
					i++;
					try {
						p.print4();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

			}
		}.start();
	}

}

class Printer3 {
	int flag = 0;

	ReentrantLock rl = new ReentrantLock();
	Condition c1 = rl.newCondition();
	Condition c2 = rl.newCondition();
	Condition c3 = rl.newCondition();
	Condition c4 = rl.newCondition();

	public void print1() throws InterruptedException {
		rl.lock();
		try {
			if (flag != 0) {
				c1.await();
			}

			System.out.print("窗");
			System.out.print("前");
			System.out.print("明");
			System.out.print("月");
			System.out.print("光");
			System.out.print("\r\n");

			flag = 1;
			c2.signal();
		} finally{
			rl.unlock();
		}
	}

	public void print2() throws InterruptedException {

		rl.lock();
		try {
			if (flag != 1) {
				c2.await();
			}

			System.out.print("疑");
			System.out.print("是");
			System.out.print("地");
			System.out.print("上");
			System.out.print("霜");
			System.out.print("\r\n");

			flag = 2;

			c3.signal();
		} finally {
			rl.unlock();
		}

	}

	public void print3() throws InterruptedException {
		rl.lock();
		try {
			if (flag != 2) {
				c3.await();
			}

			System.out.print("举");
			System.out.print("头");
			System.out.print("望");
			System.out.print("明");
			System.out.print("月");
			System.out.print("\r\n");

			flag = 3;

			c4.signal();
		} finally{
			rl.unlock();
		}

	}

	public void print4() throws InterruptedException {
		rl.lock();
		try {
			if (flag != 3) {
				c4.await();
			}

			System.out.print("低");
			System.out.print("头");
			System.out.print("思");
			System.out.print("故");
			System.out.print("乡");
			System.out.print("\r\n");

			flag = 0;

			c1.signal();
		} finally {
			rl.unlock();
		}
	}
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ang_qq_252390816

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值