Java多线程记录(包含案例)
线程是程序的执行单元,执行路径。是程序使用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<integer> queue = new linkedlist<integer>(); 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<ncust;i++) { 26 customers[i] = new customer(customerwaiting,seats,barbersleeping); 27 cthread[i] = new thread(customers[i]); 28 cthread[i].start(); 29 } 30 } 31 }