What is multithread?
multithread(多线程)可以让程序/系统同时做多件事情。用于提升效率。这里要着重介绍四个概念。
process(进程),进程具有自包含的独立运行环境(self-contained excesive environment),并且有着自己的内存空间(own memory space)。
thread(线程),线程和进程都提供了一个运行环境,但线程(thread)被包含于进程(process)中。
用白话来说,就是一个正在运行的程序便为一个进程,一个进程至少有一个线程,叫做主线程(main thread),主线程可以创造新线程。(王者在更新的时候,更新的进程为主进程,并且可以同时解析文件/发出bgm/呈现动画)
并发(concurrency),程序或系统可以在同时交替做两件或多件事。(一个核心做多件事)
并行,多个系统或程序在同时做多个事情。(多个核心做多件事)
How to use multithread?
三种实现方式
继承Thread类实现多线程
首先需要定义一个自己的线程并继承Thread类。并且重写run方法。下面的进程为输出0-99的值。
public class MyThread extends Thread{
@Override
public void run(){
for(int i= 0;i<100;i++){
System.out.println(getName()+"i ="+i);}
}
接下来我们在test类中进行测试。下面的程序为创建两个同时运行的线程。
public class TestMultiThread{
/* 1.创建MyThread对象
* 2.调用start方法进行线程运行
*/
public static void main(String[] args){
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
mt1.setName("线程1");
mt2.setName("线程2");
mt1.start();
mt2.start();}}
利用Runnable接口实现多线程
首先需要定义一个自己的类并implements Runnable接口。并且重写run方法。因为没有继承Thread类,所以不能直接用getName()方法,用到了Thread.currentThread()方法,相当于创建了一个对象为现在正在run的thread,便可以使用getName方法。
public class MyRun implements Runnable{
@Override
public void run(){
for(int i= 0;i<100;i++){
System.out.println(Thread.currentThread().getName()+"i ="+i);}
}
在test类中进行测试。下面的程序为创建两个同时运行的线程。
public class TestMultiThread{
/* 1.创建MyRun对象
* 2.创建thread对象
* 2.调用start方法进行线程运行
*/
public static void main(String[] args){
MyRun mr = new MyRun();
MyThread mt1 = new MyThread(mr);
MyThread mt2 = new MyThread(mr);
mt1.setName("线程1");
mt2.setName("线程2");
mt1.start();
mt2.start();}}
利用Callable接口和Future接口实现多线程
定义MyCall类并implements Callable接口。值得注意的是,Callable接口是有泛型的存在,只要规定了泛型,则call方法的返回值也随之确定。
public class Mycall implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int sum =0;
for(int i= 0;i<100;i++){
sum += i;
return sum;}}
在test类中进行测试。
public class Multithread {
/* 1.创建MyCall对象
* 2.创建FutureTask对象
* 3.创建thread对象
* 4.线程运行
* 5.通过FutureTask类的.get()方法得到返回值
*/
public static void main(String[] args) throws InterruptedException, ExecutionException { MyCall mc = new MyCall();
FutureTask<Integer> ft =new FutureTask<>(mc);
Thread t1 =new Thread(ft);
t1.start();
System.out.print(ft.get());
}
}
Thread类中一些方法。
String getName(); //找名字
void setName(String name); //取名字
static Thread currentThread(); //找到现在运行的Thread
static void sleep(long time); //睡眠time毫秒
void setPriority(int newpriority); //设置优先级
final int getPriority(); //找优先级
final void setDaemon(boolean ); //设置为守护线程
public static void yield(); //礼让线程
public static void join(); //插入线程
getName();setName();
Thread t = new Thread();
t.setName("AAA");
System.out.println(t.getName()); //输出AAA
currentThread();
System.out.print(Thread.currentThread().getName())
//输出现在运行进程名称
sleep();
注意,有Exception不能抛出,只能try catch。
System.out.println("AAA");
Thread.sleep(5000); //暂停5s
System.out.println("BBB");
//输出AAA,过5s输出BBB
setPriority();getPriority();
初始priority值为5,范围1-10,越大优先级越高。
Thread.setPriority(10); //设置main优先级为10
int priority =Thread.getPriority(); //查找main的优先级
setDaemon();
设置进程为守护线程,我个人称为伴随线程,非守护的线程结束后,守护线程会陆续停止。
MyThread1 t1 = new MyThread1();
MyThread2 t2 = new MyThread2();
t2.setDaemon(); //设置t2为守护线程。
t1.start();
t2.start();
如果规定t1执行10次,t2执行1000次,在t1执行结束后,t2会慢慢结束(不是立即结束)。
yield();
出让线程是为了让程序分布更均匀一些,每次运行到yield之后会出让cpu的执行。不同线程重新进行cpu抢夺。
public class MyThread extends Thread{
@Override
public void run(){
for(int i= 0;i<100;i++){
System.out.println(getName()+"i ="+i);
Thread.yield();} //每次执行后出让cpu
}
join();
public class Multithread {
public static void main(String[] args) {
MyThread t1 =new MyThread(ft);
t1.start();
t1.join(); //将t1插入到main线程之前,否则会先运行main中的code
for(int i =0;i<10;i++){
yourcode......}
}
}
MultiThread的利弊。
1.它适用于开发反应性(reactive)系统,它可以连续监测传感器阵列,并根据传感器读数对控制系统作出反应。
2.它使应用程序对用户输入的响应性(responsive)更快。
3.它允许一个服务器处理多个客户端(multiple clients)。
4.它可以通过并行地在不同的内核上执行线程来利用多个处理器内核的可用性。
与此同时,弊端也有:
1.不同线程之间的交互和合作可能会导致安全(safety)和存在性(liveness)问题。
2.由于线程创建、调度和同步的成本,多线程程序涉及到一定的开销。
example:设计一个程序通过3个窗口去卖100张票。
public class MyThread extends Thread{
static int ticketNumber =0; //类中共享static值
@Override
public void run(){
while(true){ //线程循环运行具有随机性
try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}
if(ticketNumber < 100){
ticketNumber++;
System.out.println("我们在"+Thread.getName()+"卖"+ticketNumber+"张票");}
else{break;}}}}
再在test中new3个线程,发现可能三个窗口会卖同一张票,而且有可能卖超。
同步代码块,同步方法(Sycchronization)
想要解决这个问题,我们可以将可能起冲突的代码块在一个进程执行的时候锁住,结束后开锁。
同步代码块。
public class MyThread extends Thread{
static int ticketNumber =0; //类中共享static值
@Override
public void run(){
while(true){ //线程循环运行具有随机性
Synchronized(MyThread.class){ //锁对象要唯一
try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}
if(ticketNumber < 100){
ticketNumber++;
System.out.println("我们在"+Thread.getName()+"卖"+ticketNumber+"张票");}
else{break;}}}}}
public class MyRunnable implements Runnable{
static int ticketNumber =0; //类中共享static值
@Override
public void run(){
while(true){ //线程循环运行具有随机性
Synchronized(MyRunnable.class){ //锁对象要唯一
try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}
if(ticketNumber < 100){
ticketNumber++;
System.out.println("我们在"+Thread.getName()+"卖"+ticketNumber+"张票");}
else{break;}}}}}
同步方法。
public class MyThread extends Thread{
static int ticketNumber =0; //类中共享static值
@Override
public void run(){
while(true){ //线程循环运行具有随机性
if(method()) break;}}
public synchronized static boolean method(){ //继承Thread的话要加static
if(ticketNumber == 100){return true;}
else{
try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}
ticketNumber++; syso(.....);return false;}}
public class MyRunnable implements Runnable{
static int ticketNumber =0; //类中共享static值
@Override
public void run(){
while(true){ //线程循环运行具有随机性
if(method()) break;}}
public synchronized boolean method(){ //runnable不用加static
if(ticketNumber == 100){return true;}
else{
try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}
ticketNumber++; syso(.....);return false;}}
锁(lock)
lock是一个interface,所以不能用lock来new对象,只能用实例方法来new对象。
Lock lock =new ReentrantLock();
实现方法
public class MyThread extends Thread{
static int ticketNumber =0; //类中共享static值
static Lock lock =new entrantLock(); //类中共享lock
@Override
public void run(){
while(true){ //线程循环运行具有随机性
lock.lock(); //关锁
try{Thread.sleep(1000);
if(ticketNumber < 100){
ticketNumber++;
System.out.println("我们在"+Thread.getName()+"卖"+ticketNumber+"张票");}
else{break;}
catch(InterruptedException e){e.printStackTrace();}
finally{lock.unlock();} //不管什么情况,关锁。
}
}
public class MyRunnable implements Runnable{
static int ticketNumber =0; //类中共享static值
Lock lock =new entrantLock();
@Override
public void run(){
while(true){ //线程循环运行具有随机性
lock.lock(); //关锁
try{Thread.sleep(1000);
if(ticketNumber < 100){
ticketNumber++;
System.out.println("我们在"+Thread.getName()+"卖"+ticketNumber+"张票");}
else{break;}
catch(InterruptedException e){e.printStackTrace();}
finally{lock.unlock();} //不管什么情况,关锁。
}
}
死锁(DeadLock)
两个线程都需要两个锁,但是第一个线程抢到了第一个锁,第二个线程抢到了第二个锁,线程一在等待第二个锁,线程二在等待第一个锁,就成了死锁。
代码中不要出现锁的嵌套,可以避免。资源排序(resource ordering),也可以避免。
Thread的生命周期。
一个线程分为五个阶段,new(创造对象),start后变为ready(有执行资格,没有执行权,需要抢cpu),通过run之后变为running(有执行资格,有执行权),如果顺利运行结束变为finished;如果遇到sleep或者其他block的方法,则变为blocked(没有执行资格,没有执行权)等待阻塞结束,之后重新变为ready。
在Java中,没有定义running状态,而是交给操作系统,还有两个waiting和time_waiting状态,一共六个状态。
生产者(producer)和消费者(consumer)问题。
通俗来说,一个进程生产,一个进程消费,还需要一个中间商进行判断。
中间商:
public class MiddlePerson {
/*
* 控制生产者与消费者
*/
public static int numbers = 0; //1中间人有东西,0中间人没东西。
public static int maxCount = 100;//一共交易多少次。
public static Object lock =new Object(); //定义锁
}
消费者:
public class Consumer extends Thread{
@Override
public void run(){
whlie(true){
synchronized(middlePerson.lock){
if(middlePerson.maxCount == 0){break;}
else{
if(middlePerson.numbers == 0){middlePerson.lock.wait(); //用锁对象调用wait方法,有问题trycatch掉
else{middlePerson.maxCount--;
System.out.println("消费一次,还能消费"+middlePerson.maxCount+"次");
middlePerson.lock.notifyAll(); //唤醒其他与锁相关的线程
middlePerson.numbers == 0;
}}}}}}
生产者:
public class Producer extends Thread{
@Override
public void run(){
whlie(true){
synchronized(middlePerson.lock){
if(middlePerson.maxCount == 0){break;}
else{
if(middlePerson.numbers == 1){middlePerson.lock.wait(); //用锁对象调用wait方法,有问题trycatch掉
else{
System.out.println("生产一次");
middlePerson.numbers == 1;
middlePerson.lock.notifyAll(); //唤醒其他与锁相关的线程
}}}}}}
测试代码 :
Producer p = new Producer();
Consumer c = new Consumer();
p.start();
c.start();
//pc会交替运行
标签:Java,Thread,void,ticketNumber,线程,static,Multithreading,public
From: https://blog.csdn.net/weixin_48438939/article/details/140577241