多线程
目录
-
进程和线程
- 进程:是正在运行的程序是系统进行资源分配和调用的独立单位每一个进程都有它自己的内存空间和系统资源
- 线程:是进程中的单个顺序控制流,是一条执行路径
- 单线程:一个进程如果只有一条执行路径,则称为单线程程序
- 多线程:一个进程如果有多条执行路径,则称为多线程程序
-
多线程实现-方式一
多线程实现方式一:继承Thread类,重写run()方法(Thread类实现了Runnable接口)
直接调用run()方法,相当于普通方法的调用,要想启动多个线程,需要使用start()方法,然后由jvm调用run()方法
方法名 | 说明 |
void run() | 在线程开启后,此方法将被调用执行 |
void start() | 使此线程开始执行,Java虚拟机会调用run方法() |
public static void main(String[] args) {
ThreadTest t1 = new ThreadTest();
ThreadTest t2 = new ThreadTest();
t1.setName("线程1");
t2.setName("线程2");
/* public final synchronized void setName(String name) {
this.checkAccess();
if (name == null) {
throw new NullPointerException("name cannot be null");
} else {
this.name = name;
if (this.threadStatus != 0) {
this.setNativeName(name);
}
}
}*/
t1.getName();
/* public final String getName() {
return this.name;
}*/
t1.start();
t2.start();
//线程名称为 Thread-0 的原因
/* public Thread() {
//Thread-0,1,2,3...
this((ThreadGroup)null, (Runnable)null, "Thread-" + nextThreadNum(), 0L);
}
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
*/
}
-
设置获取线程名称
public static void main(String[] args) {
//给线程命令的方式一
/* ThreadTest t1 = new ThreadTest();
ThreadTest t2 = new ThreadTest();
t1.setName("线程1");
t2.setName("线程1");*/
//给线程命令的方式二
//ThreadTest使用带参构造方法给name赋值
ThreadTest t1 = new ThreadTest("线程1");
ThreadTest t2 = new ThreadTest("线程1");
t1.start();
t2.start();
}
public class ThreadTest extends Thread {
public ThreadTest() {
}
public ThreadTest(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(this.getName() + "--" + i);
}
}
}
获取当前方法所在线程的名称
public static void main(String[] args) {
//获取当前方法所在线程的名称
// currentThread() 返回对当前正在执行的线程对象的引用。
//Thread thread = Thread.currentThread();
Thread.currentThread().setName("主线程");
System.out.println(Thread.currentThread().getName());
}
-
线程调度(设置线程优先级)
注意:优先级高的线程只是获取cpu的时间相对多一些,不是绝对的占有
public static void main(String[] args) {
// getPriority() 返回此线程的优先级。线程默认优先级为5
// setPriority(int newPriority) 更改此线程的优先级。
//Thread thread = Thread.currentThread();
System.out.println(Thread.NORM_PRIORITY); //默认优先级5
System.out.println(Thread.MIN_PRIORITY); //最低优先级1
System.out.println(Thread.MAX_PRIORITY); //最高优先级10
ThreadTest t1 = new ThreadTest();
ThreadTest t2 = new ThreadTest();
ThreadTest t3 = new ThreadTest();
//设置线程名称
t1.setName("飞机");
t2.setName("和谐号");
t3.setName("汽车");
//设置优先级 注意:优先级高的线程只是获取cpu的时间相对多一些,不是绝对的占有
t1.setPriority(10);
t2.setPriority(5);
t3.setPriority(1);
t1.start();
t2.start();
t3.start();
}
/*
飞机--0
...
飞机--16
飞机--17
和谐号--0
飞机--18
飞机--19
飞机--20
飞机--21
飞机--22
和谐号--1
飞机--23
和谐号--2
飞机--24
和谐号--3
飞机--25
飞机--26
和谐号--4
...
汽车--98
汽车--99
*/
注意:优先级高的线程只是获取cpu的时间相对多一些,不是绝对的占有,所以和谐号线程会出在飞机线程执行过程中也执行
-
线程控制
方法名 | 说明 |
static void sleep(long millis) | 使当前正在执行的线程停留(暂停执行)指定的毫秒数 |
void join() | 等待这个线程死亡 |
void setDaemon(boolean on) | 将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出 |
public static void main(String[] args) {
ThreadTest t1 = new ThreadTest();
ThreadTest t2 = new ThreadTest();
ThreadTest t3 = new ThreadTest();
//设置线程名称
t1.setName("李淳风");
t2.setName("袁天罡");
t3.setName("李茂贞");
t1.start();
try {
//等待此线程执行完成之后其它线程才能开始执行
//位置只能放置t1.start()之后,且不能在其它的线程start之后
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
t3.start();
}
public class ThreadTest extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(this.getName() + "--" + i);
/*try {
//使当前正在执行的线程停留(暂停执行)指定的毫秒数
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
}
}
public static void main(String[] args) {
ThreadTest t1 = new ThreadTest();
ThreadTest t2 = new ThreadTest();
Thread mainThread = Thread.currentThread();
mainThread.setName("李唐");
//设置线程名称
t1.setName("李淳风");
t2.setName("袁天罡");
//设置t1、t2为守护进程
//将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出
//当主线程退出后守护进程立刻也会退出
t1.setDaemon(true);
t2.setDaemon(true);
t1.start();
t2.start();
for (int i = 0; i < 10; i++) {
System.out.println(mainThread.getName() + "--" + i);
}
}
public class ThreadTest extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(this.getName() + "--" + i);
/*try {
//使当前正在执行的线程停留(暂停执行)指定的毫秒数
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
}
}
/*
李淳风--0
李淳风--1
李淳风--2
李淳风--3
李淳风--4
李淳风--5
李淳风--6
李淳风--7
李淳风--8
李淳风--9
李淳风--10
李淳风--11
李淳风--12
袁天罡--0
李淳风--13
袁天罡--1
李淳风--14
袁天罡--2
李唐--0
李淳风--15
李唐--1
袁天罡--3
李唐--2
李唐--3
李唐--4
李唐--5
李淳风--16
李淳风--17
李唐--6
李唐--7
李唐--8
李唐--9
袁天罡--4
袁天罡--5
袁天罡--6
袁天罡--7
袁天罡--8
袁天罡--9
Process finished with exit code 0
*/
-
线程的生命周期
-
多线程实现-方式二(推荐使用直接实现Runnable接口的方式)
多线程实现方式二:直接实现Runnable接口,重写run()方法
方法名 | 说明 |
Thread(Runnable target) | 分配一个新的Thread对象 |
Thread(Runnable target, String name) | 分配一个新的Thread对象 |
所以推荐使用直接实现Runnable接口的方式实现多线程
public static void main(String[] args) {
RunnableTest runnableTest = new RunnableTest();
/*//创建Thread对象,将Runnable实现类的对象作为构造方法的参数,Thread(Runnable target) 分配一个新的 Thread对象。
Thread thread1 = new Thread(runnableTest);
Thread thread2 = new Thread(runnableTest);
Thread thread3 = new Thread(runnableTest);*/
//创建Thread对象,将Runnable实现类的对象和线程名称作为构造方法的参数,Thread(Runnable target, String name) 分配一个新的 Thread对象。
Thread thread1 = new Thread(runnableTest, "线程1");
Thread thread2 = new Thread(runnableTest, "线程2");
Thread thread3 = new Thread(runnableTest, "线程3");
thread1.start();
thread2.start();
thread3.start();
}
public class RunnableTest implements Runnable {
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName() + "--" + i);
}
}
}
public class SellTicket implements Runnable{
private int ticketNum = 100;
@Override
public void run() {
boolean flag = true;
while(flag){
if (ticketNum>0){
try {
//模拟出票时间100毫秒
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + ticketNum + "张票");
ticketNum--;
}else{
System.out.println(Thread.currentThread().getName() + "票已售完");
flag = false;
}
}
}
}
public static void main(String[] args) {
SellTicket sellTicket = new SellTicket();
Thread thread1 = new Thread(sellTicket, "窗口1");
Thread thread2 = new Thread(sellTicket, "窗口2");
Thread thread3 = new Thread(sellTicket, "窗口3");
thread1.start();
thread2.start();
thread3.start();
}
-
线程同步
注意:synchronized的锁可以是任意一个对象,但是如果要让其生效只能使用同一把锁。
synchronized(任意对象) {
共享数据代码块
}
public class SellTicket implements Runnable{
private int ticketNum = 100;
private Object lock = new Object(); //多线程线程安全问题需要同一把锁才能解决问题
@Override
public void run() {
boolean flag = true;
while(flag){
synchronized(lock) {
if (ticketNum > 0) {
try {
//模拟出票时间100毫秒
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + ticketNum + "张票");
ticketNum--;
} else {
System.out.println(Thread.currentThread().getName() + "票已售完");
flag = false;
}
}
}
}
}
public static void main(String[] args) {
SellTicket sellTicket = new SellTicket();
Thread thread1 = new Thread(sellTicket, "窗口1");
Thread thread2 = new Thread(sellTicket, "窗口2");
Thread thread3 = new Thread(sellTicket, "窗口3");
thread1.start();
thread2.start();
thread3.start();
}
/*
窗口2正在出售第100张票
窗口1正在出售第99张票
...
窗口3正在出售第2张票
窗口1正在出售第1张票
窗口1票已售完
窗口2票已售完
窗口3票已售完
Process finished with exit code 0
*/
-
同步方法
普通同步方法格式:
public synchronized void methodName() {
代码块
}
普通同步方法格式:
public static synchronized void methodName() {
代码块
}
public class SellTicket implements Runnable {
private static int ticketNum = 100;
private Object lock = new Object(); //多线程线程安全问题需要同一把锁才能解决问题
private int num = 0;
@Override
public void run() {
boolean flag = true;
while (flag) {
if (num % 2 == 0) {
//synchronized(lock) { //普通锁为任意对象
//synchronized (this) { //普通方法同步锁为当前类当前对象
synchronized (SellTicket.class) { //静态方法同步锁为当前类
if (ticketNum > 0) {
try {
//模拟出票时间100毫秒
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + ticketNum + "张票");
ticketNum--;
} else {
System.out.println(Thread.currentThread().getName() + "票已售完");
flag = false;
}
}
} else {
flag = sellTicket(flag);
}
num++;
}
}
public synchronized boolean sellTicket(boolean flag) {
if (ticketNum > 0) {
try {
//模拟出票时间100毫秒
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + ticketNum + "张票");
ticketNum--;
} else {
System.out.println(Thread.currentThread().getName() + "票已售完");
flag = false;
}
return flag;
}
public static synchronized boolean sellTicketStatic(boolean flag) {
if (ticketNum > 0) {
try {
//模拟出票时间100毫秒
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + ticketNum + "张票");
ticketNum--;
} else {
System.out.println(Thread.currentThread().getName() + "票已售完");
flag = false;
}
return flag;
}
}
public static void main(String[] args) {
SellTicket sellTicket = new SellTicket();
Thread thread1 = new Thread(sellTicket, "窗口1");
Thread thread2 = new Thread(sellTicket, "窗口2");
Thread thread3 = new Thread(sellTicket, "窗口3");
thread1.start();
thread2.start();
thread3.start();
}
-
线程安全的类
- StringBuffer
- Vector
- Hashtable
/*
线程安全的类:
StringBuffer
Vector
Hashtable
*/
public class ThreadDemo {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
StringBuilder sb2 = new StringBuilder();
Vector<String> v = new Vector<String>();
ArrayList<String> array = new ArrayList<String>();
Hashtable<String,String> ht = new Hashtable<String, String>();
HashMap<String,String> hm = new HashMap<String, String>();
//多线程一般也不使用Vector,而是通过Collections.synchronizedList()方法返回由指定列表支持的同步(线程安全)列表
//static <T> List<T> synchronizedList(List<T> list) 返回由指定列表支持的同步(线程安全)列表
List<String> list = Collections.synchronizedList(new ArrayList<String>()); //list为线程安全列表
//多线程一般也不使用Hashtable,而是通过Collections.synchronizedMap方法返回由指定map支持的同步(线程安全)映射。
//synchronizedMap(Map<K,V> m) 返回由指定map支持的同步(线程安全)映射。
List<String> syncMap = Collections.synchronizedMap(new HashMap<String, String>); //syncMap为线程安全的HashMap
//同理还有方法:
// synchronizedCollection(Collection<T> c) 返回由指定集合支持的同步(线程安全)集合。
// synchronizedSet(Set<T> s) 返回由指定集合支持的同步(线程安全)集。
// synchronizedSortedSet(SortedSet<T> s) 返回由指定的排序集支持的同步(线程安全)排序集。
// synchronizedSortedMap(SortedMap<K,V> m) 返回由指定的排序映射支持的同步(线程安全)排序映射。
}
}
-
Lock锁
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
ReentrantLock构造方法
方法名 | 说明 |
ReentrantLock() | 创建一个ReentrantLock的实例 |
加锁解锁方法
方法名 | 说明 |
void lock() | 获得锁 |
void unlock() | 释放锁 |
public class SellTicket implements Runnable {
private static int ticketNum = 100;
private Lock lock = new ReentrantLock();
@Override
public void run() {
boolean flag = true;
while (flag) {
try {
lock.lock();
if (ticketNum > 0) {
try {
//模拟出票时间100毫秒
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + ticketNum + "张票");
ticketNum--;
} else {
System.out.println(Thread.currentThread().getName() + "票已售完");
flag = false;
}
} finally {
lock.unlock();
}
}
}
}
public static void main(String[] args) {
SellTicket sellTicket = new SellTicket();
Thread thread1 = new Thread(sellTicket, "窗口1");
Thread thread2 = new Thread(sellTicket, "窗口2");
Thread thread3 = new Thread(sellTicket, "窗口3");
thread1.start();
thread2.start();
thread3.start();
}
-
生产者消费者
生产者和消费者模式概述
方法名 | 说明 |
void wait() | 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法 |
void notify() | 唤醒正在等待对象监视器的单个线程 |
void notifyAll() | 唤醒正在等待对象监视器的所有线程 |
public class Bok {
//定义一个成员变量,表示第x瓶奶
private int milkNum;
//定义一个成员变量,表示奶箱的状态
private boolean status = false;
public synchronized void putMilk(int milkNum){
if (status){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.milkNum = milkNum;
System.out.println("生产者生产第: " + this.milkNum + "牛奶");
//生产完毕之后,修改奶箱状态
this.status = true;
//唤醒其他等待的线程
notifyAll();
}
public synchronized void getMilk(){
if (!status){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.milkNum = milkNum;
System.out.println("消费者消费第: " + this.milkNum + "牛奶");
//消费完毕之后,修改奶箱状态
this.status = false;
//唤醒其他等待的线程
notifyAll();
}
}
public class Producer implements Runnable{
private Bok b;
public Producer() {
}
public Producer(Bok b) {
this.b = b;
}
@Override
public void run() {
for(int i=0;i<10;i++){
b.putMilk(i);
}
System.out.println("牛奶以生产完成!");
}
}
public class Custumer implements Runnable{
private Bok b;
public Custumer() {
}
public Custumer(Bok b) {
this.b = b;
}
@Override
public void run(){
boolean flag = true;
while(flag){
b.getMilk();
}
}
}
public static void main(String[] args) {
//创建奶箱对象,这是共享数据区域
Bok b = new Bok();
//创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用存储牛奶的操作
Producer prod = new Producer(b);
//创建消费者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用获取牛奶的操作
Custumer cust = new Custumer(b);
//创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
Thread producer = new Thread(prod);
Thread custumer = new Thread(cust);
//启动线程
producer.start();
custumer.start();
}
/*
生产者生产第: 0牛奶
消费者消费第: 0牛奶
生产者生产第: 1牛奶
消费者消费第: 1牛奶
生产者生产第: 2牛奶
消费者消费第: 2牛奶
生产者生产第: 3牛奶
消费者消费第: 3牛奶
生产者生产第: 4牛奶
消费者消费第: 4牛奶
生产者生产第: 5牛奶
消费者消费第: 5牛奶
生产者生产第: 6牛奶
消费者消费第: 6牛奶
生产者生产第: 7牛奶
消费者消费第: 7牛奶
生产者生产第: 8牛奶
消费者消费第: 8牛奶
生产者生产第: 9牛奶
消费者消费第: 9牛奶
牛奶以生产完成!
*/