线程是程序的执行单元,执行路径。是程序使用CPU的最基本单位
多线程 — 程序有多条执行路径,提高应用进程的使用率
进程中线程越多,抢到CPU执行权概率越高。线程的执行有随机性
Java程序的运行原理:
由Java命令启动JVM,JVM启动就相当于启动了一个进程,接着由该程序创建了一个主线程去调用main方法
JVM的启动是多线程的,因为垃圾回收线程也要启动,否则容易出现内存溢出。(GC线程和主线程)
Java多线程程序:
方法1:继承Thread类,并重写run()方法,创建对象,执行对象。
run()方法: Thread类中的run()用来包含那些被线程执行的代码(封装被线程执行的代码)(不单独调用run方法(直接调用是普通方法),调用start())
start()方法:首先启动了线程,然后再由JVM去调用该线程的run()方法
方法2:实现runnable接口
A:自定义类实现runnable
B:重写run()方法
C:创建MyRunnable类的对象
D:创建Thread类的对象,把C步骤的对象作为构造参数传递
解决了java单继承的局限性,适合多个相同程序代码去处理同一个资源的情况,把线程相同的代码,数据有效分离
一些内建方法:
public final String getName() //获取线程名称
public final String setName(): //设置线程名称
public static Thread currentThread(): //返回当前正在执行的线程对象
抢占式调度模型(Java):优先让优先级高的线程使用CPU
public final int getPriority(): //返回线程对象的优先级
public final void setPriority(): //设置线程优先级
优先级范围1-10,默认为5,最低为1,最高为5
线程控制:
public static void sleep(long millis) //线程休眠
public final void join(): //线程加入 (等待线程终止)
public static void yield(): //线程礼让 暂停当前正在执行的线程对象,并执行其他线程//让多个线程的执行更加和谐,但不能保证
public final void setDaemon(boolean on): //设置守护线程
//当正在运行的线程都是守护线程时,JVM退出,该方法必须在启动线程前调用
public void interrupt(): //中断线程,把线程的状态终止,并抛出一个InterruptException
线程的生命周期:(图解)
线程同步:synchronized关键字
将多条语句操作的共享数据代码包成一个整体,让某个线程在执行的时候别人不执行
前提:多个线程; 注意:多个线程使用同一个锁对象
优点:解决了多线程的安全问题
缺点:当线程相当时,因为每个线程都会去判断同步上的锁,很耗费资源,无形中降低了程序的运行效率,容易产生死锁
使用同步代码块或者同步方法
死锁:
死锁代码举例:
1 public class MyLock {
2 // 创建两把锁对象
3 public static final Object objA = new Object();
4 public static final Object objB = new Object();
5 }
1 public class DeadLock extends Thread {
2
3 private boolean flag;
4
5 public DeadLock(boolean flag) {
6 this.flag = flag;
7 }
8
9 @Override
10 public void run() {
11 if (flag) {
12 synchronized (MyLock.objA) {
13 System.out.println("if objA");
14 synchronized (MyLock.objB) {
15 System.out.println("if objB");
16 }
17 }
18 } else {
19 synchronized (MyLock.objB) {
20 System.out.println("else objB");
21 synchronized (MyLock.objA) {
22 System.out.println("else objA");
23 }
24 }
25 }
26 }
27 }
1 public class DieLockDemo {
2 public static void main(String[] args) {
3 DeadLock dl1 = new DeadLock(true);
4 DeadLock dl2 = new DeadLock(false);
5
6 dl1.start();
7 dl2.start();
8 }
9 }
等待唤醒机制:
Object类提供三个方法:
wait() :等待
notify():唤醒单个线程
notifyAll():唤醒所有线程
为什么不在Thread类中: 因为这些方法必须通过锁对象调用,而锁对象可以是任意锁对象,所以定义在object类中
Semaphore信号量:
信号量(Semaphore),又被称为信号灯,在多线程环境下用于协调各个线程, 以保证它们能够正确、合理的使用公共资源。信号量维护了一个许可集,我们在初始化Semaphore时需要为这个许可集传入一个数量值,该数量值代表同一时间能访问共享资源的线程数量。
线程可以通过acquire()方法获取到一个许可,然后对共享资源进行操作,注意如果许可集已分配完了,那么线程将进入等待状态,直到其他线程释放许可才有机会再获取许可,线程释放一个许可通过release()方法完成,”许可”将被归还给Semaphore。
多线程案例:
电影院卖票:电影院一共有100张票,有三个窗口卖票,模拟电影院售票:
使用同步代码块:
public class SellTicket implements Runnable {
// 定义100张票
private int tickets = 100;
//创建锁对象
private Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "正在出售第" + (tickets--) + "张票");
}
}
}
}
}
public class SellTicketDemo {
public static void main(String[] args) {
// 创建资源对象
SellTicket st = new SellTicket();
// 创建三个线程对象
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");
// 启动线程
t1.start();
t2.start();
t3.start();
}
}
使用同步方法:
1 package lab2;
2
3 public class SellTicket implements Runnable {
4
5 // 定义100张票
6 private static int tickets = 100;
7
8 @Override
9 public void run() {
10 while (true) {
11 sellTicket();
12 }
13 }
14
15 private synchronized void sellTicket() {
16 if (tickets > 0) {
17 try {
18 Thread.sleep(100);
19 } catch (InterruptedException e) {
20 e.printStackTrace();
21 }
22 System.out.println(Thread.currentThread().getName()
23 + "正在出售第" + (tickets--) + "张票 ");
24 }
25 }
26 }
使用锁方法:
1 import java.util.concurrent.locks.Lock;
2 import java.util.concurrent.locks.ReentrantLock;
3
4 public class SellTicket implements Runnable {
5
6 // 定义票
7 private int tickets = 100;
8
9 // 定义锁对象
10 private Lock lock = new ReentrantLock();
11
12 @Override
13 public void run() {
14 while (true) {
15 try {
16 // 加锁
17 lock.lock();
18 if (tickets > 0) {
19 try {
20 Thread.sleep(100);
21 } catch (InterruptedException e) {
22 e.printStackTrace();
23 }
24 System.out.println(Thread.currentThread().getName()
25 + "正在出售第" + (tickets--) + "张票");
26 }
27 } finally {
28 // 释放锁
29 lock.unlock();
30 }
31 }
32 }
33
34 }
Ornamental Garden Problem:(使用Bakery class) 花园有两个旋转门,可进可出,监控花园内的人数
volatile关键字: Specifying a variable as volatile instructs the compiler to load and store the value of the variable at each use. 另外更多说明可以参考:https://www.cnblogs.com/daxin/p/3364014.html
Bakery Algorithm面包店算法
Bakery.java
1 package lab2; 2 3 public class Bakery { 4 public int nThreads; 5 6 private volatile int[] ticket; 7 private volatile boolean[] choosing; 8 9 Bakery(int nTh) { 10 nThreads = nTh; 11 ticket = new int[nThreads]; 12 choosing = new boolean[nThreads]; 13 14 for (int i = 0; i < nThreads; i++) { 15 System.out.println(i + ticket[1]); 16 ticket[i] = 0; 17 choosing[i] = false; 18 } 19 } 20 21 private int getNextNumber() { 22 int largest = 0; 23 for (int i = 0; i < nThreads; i++) { 24 if (ticket[i] > largest) 25 largest = ticket[i]; 26 } 27 return largest + 1; 28 } 29 30 private boolean isFavouredThread(int i, int j) { 31 if ((ticket[i] == 0) || (ticket[i] > ticket[j])) 32 return false; 33 else { 34 if (ticket[i] < ticket[j]) 35 return true; 36 else 37 return (i < j); 38 } 39 } 40 41 void wantToEnterCS(int threadID) { 42 choosing[threadID] = true; 43 ticket[threadID] = getNextNumber(); 44 choosing[threadID] = false; 45 46 for (int otherThread = 0; otherThread < nThreads; otherThread++) { 47 while(choosing[otherThread]) { 48 // busy-wait 49 } 50 // Break any ties between threads 51 while (isFavouredThread(otherThread,threadID)) { 52 // busy-wait 53 } 54 } 55 } 56 57 void exitCS(int threadID) { 58 // Leave critical section 59 ticket[threadID] = 0; 60 } 61 }
Couter.java
1 package lab2;
2
3 public class Counter {
4 volatile int value = 0;
5
6 Counter() {
7 System.out.println("TOTAL: " + value);
8 }
9
10 boolean increment() {
11 int temp = value; //read[v]
12 Simulate.HWinterrupt();
13 value = temp + 1; //write[v+1]
14 System.out.println("TOTAL: " + value);
15 return true;
16 }
17
18 boolean decrement() {
19 int temp = value; //read[v]
20 Simulate.HWinterrupt();
21 if (temp == 0) return false;
22 Simulate.HWinterrupt();
23 value = temp - 1; //write[v+1]
24 System.out.println("TOTAL: " + value);
25 return true;
26 }
27 }
28
29 class Simulate {
30 public static void HWinterrupt() {
31 if (Math.random() < 0.5)
32 try{
33 Thread.sleep(200);
34 } catch(InterruptedException ie) {};
35 }
36 }
Turnstile.java
1 package lab2;
2
3 /* Notes:
4 * No modifications need be made to the bakery class. Instead, the turnstile
5 * class and counter classes are adjusted to allow for "exit" turnstiles as well
6 * as entrance turnstiles. This solution incorporates a system to prevent the
7 * number of people in the gardens from being negative, so the sleep call in
8 * this class can be commented out. Study the code in the Counter and Turnstile
9 * classes to see how how this works.
10 * */
11
12 public class Gardens {
13 static Turnstile west;
14 static Turnstile east;
15 static Turnstile westExit;
16 static Turnstile eastExit;
17 static Counter people;
18 static Bakery bakery;
19
20 public static void main(String[] args) {
21 people = new Counter();
22 bakery = new Bakery(4);
23
24 west = new Turnstile(0, true, people, bakery);
25 east = new Turnstile(1, true, people, bakery);
26
27 westExit = new Turnstile(2, false, people, bakery);
28 eastExit = new Turnstile(3, false, people, bakery);
29
30 west.start();
31 east.start();
32
33 /*
34 try {
35 Thread.sleep(5000);
36 } catch (InterruptedException e) {
37 }
38 */
39
40 westExit.start();
41 eastExit.start();
42 }
43 }
Garden.java
1 package lab2;
2
3 public class Gardens {
4 static Turnstile west;
5 static Turnstile east;
6 static Turnstile westExit;
7 static Turnstile eastExit;
8 static Counter people;
9 static Bakery bakery;
10
11 public static void main(String[] args) {
12 people = new Counter();
13 bakery = new Bakery(4);
14
15 west = new Turnstile(0, true, people, bakery);
16 east = new Turnstile(1, true, people, bakery);
17
18 westExit = new Turnstile(2, false, people, bakery);
19 eastExit = new Turnstile(3, false, people, bakery);
20
21 west.start();
22 east.start();
23
24 /*
25 try {
26 Thread.sleep(5000);
27 } catch (InterruptedException e) {
28 }
29 */
30
31 westExit.start();
32 eastExit.start();
33 }
34 }
生产者消费者,操作列表:
Buffer.java
1 import java.util.LinkedList;
2 import java.util.NoSuchElementException;
3
4 class Buffer {
5 LinkedList queue = new LinkedList();
6
7 public synchronized void write(int i) {
8 queue.add(i);
9 }
10
11 public synchronized int read() {
12 try {
13 return queue.removeFirst();
14 } catch(NoSuchElementException e) {
15 // the buffer is empty!?
16 return -1;
17 }
18 }
19 }
Producer.java
1 class Producer implements Runnable {
2 Buffer buffer;
3
4 public Producer(Buffer b) {
5 buffer = b;
6 }
7
8 public void run() {
9 for(int i = 0; i < 20; i++) {
10 buffer.write(i);
11 System.out.println("Thread " + Thread.currentThread().getId() +
12 " writes " + i);
13 }
14 }
15 }
Consumer.java
1 class Consumer implements Runnable {
2 Buffer buffer;
3
4 public Consumer(Buffer b) {
5 buffer = b;
6 }
7
8 public void run() {
9 for(int i = 0; i < 10; i++) {
10 try {
11 Thread.sleep(10);
12 } catch (InterruptedException e) {
13 }
14 int x = buffer.read();
15 System.err.println("Thread " + Thread.currentThread().getId() +
16 " reads " + x);
17 }
18 }
19 }
InfBuffer.java
1 public class InfBuffer {
2 public static void main(String args[]) {
3 Buffer b = new Buffer();
4 Consumer c1 = new Consumer(b);
5 Consumer c2 = new Consumer(b);
6 Producer p = new Producer(b);
7 (new Thread(c1)).start();
8 (new Thread(c2)).start();
9 (new Thread(p)).start();
10 }
11 }
生产者消费者,修改学生姓名和年龄并获取:(只能一打一大片,无法实现生产者生产后等待消费者消费后再产生)
1 public class Student {
2 String name;
3 int age;
4 }
1 public class SetThread implements Runnable {
2
3 private Student s;
4 private int x = 0;
5
6 public SetThread(Student s) {
7 this.s = s;
8 }
9
10 @Override
11 public void run() {
12 while (true) {
13 synchronized (s) {
14 if (x % 2 == 0) {
15 s.name = "Student1";
16 s.age = 27;
17 } else {
18 s.name = "Student2";
19 s.age = 30;
20 }
21 x++;
22 }
23 }
24 }
25 }
1 public class GetThread implements Runnable {
2 private Student s;
3
4 public GetThread(Student s) {
5 this.s = s;
6 }
7
8 @Override
9 public void run() {
10 while (true) {
11 synchronized (s) {
12 System.out.println(s.name + "---" + s.age);
13 }
14 }
15 }
16 }
1 public class StudentDemo {
2 public static void main(String[] args) {
3 //创建资源
4 Student s = new Student();
5
6 //设置和获取的类
7 SetThread st = new SetThread(s);
8 GetThread gt = new GetThread(s);
9
10 //线程类
11 Thread t1 = new Thread(st);
12 Thread t2 = new Thread(gt);
13
14 //启动线程
15 t1.start();
16 t2.start();
17 }
18 }
生产者消费者,修改学生姓名和年龄并获取:(使用等待唤醒机制改进)
1 public class Student {
2 String name;
3 int age;
4 boolean flag; //添加一个标记,默认为false,表示生产者是否生成值
5 }
1 public class SetThread implements Runnable {
2
3 private Student s;
4 private int x = 0;
5
6 public SetThread(Student s) {
7 this.s = s;
8 }
9
10 @Override
11 public void run() {
12 while (true) {
13 synchronized (s) {
14 //判断有没有
15 if(s.flag) {
16 try {
17 s.wait();
18 } catch (InterruptedException e) {
19 e.printStackTrace();
20 }
21 }
22
23 if (x % 2 == 0) {
24 s.name = "Student1";
25 s.age = 27;
26 } else {
27 s.name = "Student2";
28 s.age = 30;
29 }
30 x++;
31
32 //修改标记
33 s.flag = true;
34 //唤醒线程
35 s.notify();
36 }
37 }
38 }
49 }
1 public class GetThread implements Runnable {
2 private Student s;
3
4 public GetThread(Student s) {
5 this.s = s;
6 }
7
8 @Override
9 public void run() {
10 while (true) {
11 synchronized (s) {
12 if (!s.flag) {
13 try {
14 s.wait();
15 } catch (InterruptedException e) {
16 e.printStackTrace();
17 }
18 }
19 System.out.println(s.name + "---" + s.age);
20
21 //修改标记
22 s.flag = false;
23 //唤醒线程
24 s.notify();
25
26 }
27 }
28 }
29 }
public class StudentDemo { //同上
public static void main(String[] args) {
//创建资源
Student s = new Student();
//设置和获取的类
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);
//线程类
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt);
//启动线程
t1.start();
t2.start();
}
}
厕所排队(使用信号量)
1 import java.util.Random;
2 import java.util.concurrent.Semaphore;
3
4 class Wc extends Thread {
5 private String name;
6 private Semaphore wc;
7
8 public Wc(String name, Semaphore wc) {
9 this.name = name;
10 this.wc = wc;
11 }
12
13 @Override
14 public void run() {
15 int availablePermit = wc.availablePermits();
16 if (availablePermit > 0) {
17 System.out.println(name+",好开心啊,我终于有坑了");
18 }else {
19 System.out.println(name+"怎么没有坑了。。。");
20 }
21
22 try {
23 wc.acquire();
24 System.out.println(name+",好开心啊,我终于抢到了!");
25 Thread.sleep(new Random().nextInt(1000));
26 System.out.println(name+",好爽啊,终于上完了!");
27 wc.release();
28 } catch (InterruptedException e) {
29
30 }
31 }
32 }
33
34 public class Demo {
35 public static void main(String[] args) {
36 Semaphore semaphore = new Semaphore(3);
37
38 for (int i = 0; i < 10; i++) {
39 Wc wc = new Wc("第"+i+"个人", semaphore);
40 wc.start();
41 }
42 }
43 }
The Sleeping Barber (生产者消费者,信号量)
1 import java.util.concurrent.Semaphore;
2
3 public class Barber implements Runnable{
4
5 Semaphore customerWaiting, seats, barberSleeping;
6
7 Barber(Semaphore customerWaiting, Semaphore seats, Semaphore barberSleeping) {
8 this.customerWaiting = customerWaiting;
9 this.seats = seats;
10 this.barberSleeping = barberSleeping;
11 }
12
13 public void run() {
14
15 while (true){
16
17 // Get a customer, sleep otherwise
18 try {
19 customerWaiting.acquire();
20 } catch (InterruptedException e) {}
21
22 // Cut the hair of the customer
23 System.out.println("Cutting Hair");
24 barberSleeping.release();
25
26 }
27 }
28 }
1 import java.util.concurrent.Semaphore;
2
3 public class Customer implements Runnable{
4
5 Semaphore customerWaiting, seats, barberSleeping;
6
7 boolean cut = false;
8 Customer(Semaphore customerWaiting, Semaphore seats, Semaphore barberSleeping) {
9 this.customerWaiting = customerWaiting;
10 this.seats = seats;
11 this.barberSleeping = barberSleeping;
12
13 }
14
15 public void run() {
16 while (!cut) {
17
18 // A random delay
19 // Don't want all the threads trying at once!
20 try {
21 Thread.sleep((long)(Math.random()*100));
22 } catch (InterruptedException e1) {}
23
24 // Try to get a seat in the waiting room
25 try {
26 seats.acquire();
27 } catch (InterruptedException e) {}
28 System.out.println(seats.availablePermits());
29
30 System.out.println(Thread.currentThread().getName()+" is sitting down");
31 // Try and wake barber
32 customerWaiting.release();
33
34
35 // Get hair cut
36 try {
37 barberSleeping.acquire();
38 } catch (InterruptedException e) {}
39 cut = true;
40 seats.release();
41
42 }
43 System.out.println(Thread.currentThread().getName()+" has had hair cut");
44 }
45 }
1 import java.util.concurrent.Semaphore;
2
3 public class Runner {
4
5 public static void main(String[] args) {
6
7 Semaphore barberSleeping = new Semaphore(1);
8 Semaphore customerWaiting = new Semaphore(1);
9 try {
10 customerWaiting.acquire();
11 barberSleeping.acquire();
12 } catch (InterruptedException e) {
13 }
14
15 Semaphore seats = new Semaphore(3);
16
17 Barber bar = new Barber(customerWaiting,seats,barberSleeping);
18 Thread bThread = new Thread(bar);
19 bThread.start();
20
21 int nCust = 30;
22 Customer customers[] = new Customer[nCust];
23 Thread cThread[] = new Thread[nCust];
24
25 for (int i=0;i) {
26 customers[i] = new Customer(customerWaiting,seats,barberSleeping);
27 cThread[i] = new Thread(customers[i]);
28 cThread[i].start();
29 }
30 }
31 }
线程池:
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互,而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
1 public class ExecutorsDemo {
2 public static void main(String[] args) {
3 // 创建一个线程池对象,控制要创建几个线程对象。
4 // public static ExecutorService newFixedThreadPool(int nThreads)
5 ExecutorService pool = Executors.newFixedThreadPool(2); //直接通过类名调用
6
7 // 可以执行Runnable对象或者Callable对象代表的线程
8 pool.submit(new MyRunnable());
9 pool.submit(new MyRunnable());
10
11 //结束线程池
12 pool.shutdown();
13 }
14 }
使用Callable方法
public class MyCallable implements Callable {
@Override
public Object call() throws Exception {
for (int x = 0; x < 100; x++) {
System.out.println(Thread.currentThread().getName() + ":" + x);
}
return null;
}
}
public class CallableDemo {
public static void main(String[] args) {
//创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);
//可以执行Runnable对象或者Callable对象代表的线程
pool.submit(new MyCallable());
pool.submit(new MyCallable());
//结束
pool.shutdown();
}
}
Callable带返回值:
1 public class CallableDemo {
2 public static void main(String[] args) throws InterruptedException, ExecutionException {
3 // 创建线程池对象
4 ExecutorService pool = Executors.newFixedThreadPool(2);
5
6 // 可以执行Runnable对象或者Callable对象代表的线程
7 Future f1 = pool.submit(new MyCallable(100));
8 Future f2 = pool.submit(new MyCallable(200));
9
10 // V get()
11 Integer i1 = f1.get();
12 Integer i2 = f2.get();
13
14 System.out.println(i1);
15 System.out.println(i2);
16
17 // 结束
18 pool.shutdown();
19 }
20 }
1 public class MyCallable implements Callable {
2
3 private int number;
4
5 public MyCallable(int number) {
6 this.number = number;
7 }
8
9 @Override
10 public Integer call() throws Exception {
11 int sum = 0;
12 for (int x = 1; x ) {
13 sum += x;
14 }
15 return sum;
16 }
17
18 }
匿名内部类实现多线程:
1 public class ThreadDemo {
2 public static void main(String[] args) {
3 // 继承Thread类来实现多线程
4 new Thread() {
5 public void run() {
6 for (int x = 0; x < 100; x++) {
7 System.out.println(Thread.currentThread().getName() + ":"
8 + x);
9 }
10 }
11 }.start();
12
13 // 实现Runnable接口来实现多线程
14 new Thread(new Runnable() {
15 @Override
16 public void run() {
17 for (int x = 0; x < 100; x++) {
18 System.out.println(Thread.currentThread().getName() + ":"
19 + x);
20 }
21 }
22 }) {
23 }.start();
24
25 // 更有难度的
26 new Thread(new Runnable() {
27 @Override
28 public void run() {
29 for (int x = 0; x < 100; x++) {
30 System.out.println("hello" + ":" + x);
31 }
32 }
33 }) {
34 public void run() { //走的是这边的run方法
35 for (int x = 0; x < 100; x++) {
36 System.out.println("world" + ":" + x);
37 }
38 }
39 }.start();
40 }
41 }
Original: https://www.cnblogs.com/AICROC/p/12813570.html
Author: 生于思考
Title: Java多线程记录(包含案例)
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/643065/
转载文章受原作者版权保护。转载请注明原作者出处!