Java多线程:
学习内容:
- 多线程概念
- Java实现多线程
- 并发中的同步机制
- 案例:多线程实现死锁
- 补充
1.多线程概念:
线程是操作系统进行资源分配的基本单位。例如使用QQ时,可以在视频电话的同时发送信息。QQ是一个进程,该进程中的视频电话的程序运行是进程中的一个线程,该进程中的发送信息也是进程中的一个线程。
因此,线程很重要。如果一个进程只有单线程,单线程内有下载任务,只有等下载完成后,该线程才能继续往下走。
2.Java实现多线程:
两种方法(1)Java实现多线程可以通过继承Threa类,并重写Run方法
(2)实现Runnable接口,并重写Run方法
第一种:继承Thread类
public class testThread extends Thread {
@Override
public void run() {
for (int i=0;i<20;i++){
System.out.println("新建线程执行"+i);
}
}
public static void main(String[] args) {
testThread testThread=new testThread();
testThread.start();
for (int i=0;i<20;i++){
System.out.println("主线程执行"+i);
}
}
}
运行结果
主线程并没有等待新建的线程完成之后再执行for循环,两个线程是在同时运行的。
第二种:实现Runnable接口
public class testRunable implements Runnable {
@Override
public void run() {
for (int i=0;i<20;i++){
System.out.println("新建线程执行"+i);
}
}
public static void main(String[] args) {
testRunable testRunable=new testRunable();
Thread thread=new Thread(testRunable);
thread.start();
for (int i=0;i<20;i++){
System.out.println("主线程执行"+i);
}
}
}
3.并发中的同步机制:
多线程存在的问题:多线程操作共享资源时不安全。例如一个进程中的两个线程都是购买火车票的线程,第一个线程看到还有一张票,准备执行下一句代码,此时第二个线程同样看到还有一张票,也准备执行下一句代码。两个线程都执行票数减一操作,最后票数成为-1。
解决问题:对于共享资源,使用synchronized对共享资源上锁,简单来说就是实现同步操作。对于上面的例子,将检查票数和票数减一上个锁,当一个线程进入检查票数后,其他线程不能进入检查票数。
同步的实现代码
public class testSynchronized implements Runnable {
private static int tickerNums=200;
@Override
public void run() {
while (true){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}//让线程沉睡一会,能够放大多线程的效果
//将检查票数和票数减一上锁,该操作一气呵成,所有线程共享static变量tickerNums
synchronized ((Object) tickerNums){
if(tickerNums<=0) {
System.out.println("没票了");
break;
}
System.out.println(Thread.currentThread().getName()+"->抢到了票"+tickerNums--);
}
}
}
public static void main(String[] args) {
testRunable testRunable=new testRunable();
new Thread(testRunable,"A").start();//新建一个A线程
new Thread(testRunable,"B").start();//新建一个B线程
new Thread(testRunable,"C").start();//新建一个C线程
}
}
同步结果
异步结果
对于C线程,已经将票数减为1,但对于A线程,仍然以为票数为12,所以进行了抢票。
Synchronized也可以用在方法上,即多个线程调用方法时必须按顺序调用,不过容易导致把不需要同步的代码同步了,降低效率。
4.多线程实现死锁:
开两个线程,设置一个共享资源,使两个线程呈现请求并占有,互相等待的状态
public class lock implements Runnable{
private static int num=1000;//总资源数 总共1000个资源,假如每个线程需要500个才能结束
private static int cnt=100;//控制等待资源输出次数
private int mynum=0;//获得资源数
@Override
public void run() {
//一共1000个资源,只有等线程获取900个资源时才退出,两个线程一般情况下会是一个线程七八百资源,一个两三百资源,然后互相等待并请求保持
while(true){
if(num<=0&&mynum!=900){
synchronized((Object)num){
cnt--;
if(cnt<=0) break;
}
System.out.println(Thread.currentThread().getName()+"正在等待资源");
}
else if(num>0&&mynum!=900){
synchronized((Object)num){
System.out.println(Thread.currentThread().getName()+"获取了资源,目前资源数"+mynum);
mynum++;
num--;
}
}
else if(mynum==900){
synchronized((Object)num){
System.out.println("资源足够,释放资源");
num+=2;
break;
}
}
}
}
public static void main(String[] args) {
new Thread(new lock(),"A").start();
new Thread(new lock(),"B").start();
}
}
运行结果
5.补充:
(1)lamda表达式
public class testLamda {
public static void main(String[] args) {
new Thread(()->{
System.out.println("新建线程输出");
}).start();
System.out.println("主线程输出");
}
}
()->{},这句话表示了实现Run方法,实现代码为花括号里的代码。()代表实现的函数,传参为空。
使用条件:只适用于接口,接口只有一个方法。
好处:核心代码直接展示,代码简洁。
(2)Thread方法(转自菜鸟教程)
(3)守护线程
A线程守护B线程。B线程结束后,A线程结束。
实现过程
新建A、B线程。
主函数内:
A线程setDaemon(true),.
A、B线程调用Start启动。