09java基础进阶之多线程、同步
原创
©著作权归作者所有:来自51CTO博客作者Denial_learn的原创作品,请联系作者获取转载授权,否则将追究法律责任
进程和线程:
进程:
- 应用程序的执行实例
- 有独立的内存空间和系统资源
线程:
- CPU调度和分派的基本单位
- 进程中执行运算的最小单位,可完成一个独立的顺序控制流程
多线程:
- 如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为“多线程”
- 多个线程交替占用CPU资源,而非真正的并行执行
好处:
- 充分利用CPU的资源
- 简化编程模型
- 带来良好的用户体验
主线程:
Java提供了java.lang.Thread类支持多线程编程
- main()方法即为主线程入口
- 产生其他子线程的线程
- 必须最后完成执行,因为它执行各种关闭动作
线程的创建和启动:
1.继承java.lang.Thread类
public class Mythread extends Thread{//继承Thread主线程
public void run(){//重写run方法
for (int i = 0; i <=20; i++) {
System.out.println("我是线程"+i+"名字是"+Thread.currentThread().getName());//getname获取该线程的名字
}
}
}
public class Test {ic static void main(String[] args) {
// TODO Auto-generated method stub
Mythread thread = new Mythread();
Mythread thread1 = new Mythread();
thread.start();//通过start方法启动线程,将执行run方法,run方法是对继承父类的run方法进行重写的
thread1.start();
}
}
2.实现java.lang.Runnable接口
public class Mythread implements Runnable{//通过实现runnable结构
@Override
public void run() {//重写run方法
// TODO Auto-generated method stub
for (int i = 0; i <=20; i++) {
System.out.println("我是线程"+i+"名字是"+Thread.currentThread().getName());
}
}
}
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread thread1 = new Thread(new Mythread(),"lxy");
Thread thread2 = new Thread(new Mythread(),"mhy");//内部传递的是对象,后边的参数是对该线程进行命名
thread1.setPriority(10);//优先级(1-10)不是说优先级大的就先执行它,而是优先级大的优先执行,执行的概率比较大
thread2.setPriority(1);
thread1.start();
thread2.start();//通过start方法启动线程,将执行run方法,run方法是对实现接口的run方法进行重写的
}
}
比较两种创建线程的方式:
- 编写简单,可直接操作线程
- 适用于单继承
- 避免单继承局限性
- 便于共享资源
线程的状态:(重点)
线程调度的方法:
线程调度指按照特定机制为多个线程分配CPU的使用权
线程休眠(sleep方法):
- 让线程暂时睡眠指定时长,线程进入阻塞状态
- 睡眠时间过后线程会再进入可运行状态
public class Wait {
public static void bySec(long s) {
for (int i = 0; i < s; i++) {
System.out.println(i + 1 + "秒");
try {
Thread.sleep(1000); //中间停顿多久(单位:毫秒)
} catch (InterruptedException e) {
e.printStackTrace();
}}}}
线程的强制运行(join方法):
使当前线程暂停执行,等待其他线程结束后再继续执行本线程
举一个例子:
医院看病,分为普通号和加急号。
加急号进行的时间比普通号的时间长。
在普通号进行到10号之前,要求将加急号进行完毕。
有50个普通号,10个加急号。
public class Test2 implements Runnable{
int i=0;
int j=0;
static Thread thread1;
static Thread thread2;//先定义两个线程
public static void main(String[] args) {//这里对方法进行实现,可以先看下面的run方法。这里采用的是实现接口的方法去实现多线程
Test2 sthread1=new Test2();
Test2 sthread2=new Test2();//先定义两个本类的对象,方便下面建立两个线程
thread1=new Thread(sthread1,"普通号");
thread1.setPriority(Thread.MIN_PRIORITY);
thread2=new Thread(sthread2,"加急号");
thread2.setPriority(Thread.MAX_PRIORITY);
thread1.start();
thread2.start();
}
public void run() {
// TODO Auto-generated method stub
if(Thread.currentThread().getName().equals("普通号")){//判断是否为线程是否针对的是普通号
for(;i<50;i++){
System.out.println("普通号第"+(i)+"号");
try {
Thread.sleep(500);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
if(i==9){
try {
thread2.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}else{
for(;j<10;j++){
System.out.println("加急号第"+(j+1)+"号");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
线程的礼让(yield方法):
- 暂停当前线程,允许其他具有相同优先级的线程获得运行机会
- 该线程处于就绪状态,不转为阻塞状态
public class MyThread implements Runnable{
public void run(){
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().
getName()+"正在运行:"+i);
if(i==3){
System.out.print("线程礼让:");
Thread.yield(); } } }}//使用yield方法使用礼让
public static void main(String[] args) {
MyThread my = new MyThread();
Thread t1 = new Thread(my,"线程A");
Thread t2 = new Thread(my,"线程B");
t1.start();
t2.start();}
同步:(明天加急更新)
- 使用synchronized修饰的方法控制对类成员变量的访问
- synchronized就是为当前的线程声明一个锁
访问修饰符 synchronized 返回类型 方法名(参数列表){……}
或者
synchronized 访问修饰符 返回类型 方法名(参数列表){……}
- 使用synchronized关键字修饰的代码块
- syncObject为需同步的对象,通常为this
synchronized(syncObject){
//需要同步的代码
}
同一时刻只能有一个线程进入synchronized(this)同步代码块
当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定
当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码
线程安全的类型:
常见类型对比:
继承关系:实现了Map接口,Hashtable继承Dictionary类
线程安全,效率较低
键和值都不允许为null
继承关系:实现了Map接口,继承AbstractMap类
非线程安全,效率较高
键和值都允许为null
前者线程安全,后者非线程安全
举例说明,同步的实现(将锁synchronized在不同的地方使用):
多线程实现网络购票,用户提交购票信息后。(“桃跑跑”、“张票票”、“黄牛党”共同抢50张票)
将锁放到方法上使用:
public class Mythread implements Runnable {
private int coun;
private int sum = 50;
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
if (!sale()) {
break;
}
}
}
public synchronized boolean sale(){
if (sum<=0) {
return false;
}
coun++;
sum--;
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"购买了,第"+coun+"张票,还剩"+sum+"几张");
return true;
}
}
使用synchronized代码块:
public class My implements Runnable {
private int coun;
private int sum = 50;
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
synchronized (this) {
if (sum <= 0) {
break;
}
coun++;
sum--;
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "购买了,第"
+ coun + "张票,还剩" + sum + "几张");
}
}
}
}
测试代码 main();
public static void main(String[] args) {
// TODO Auto-generated method stub
Mythread my = new Mythread();
Thread thread0 = new Thread(my,"套泡泡");
Thread thread1 = new Thread(my,"黄牛党");
Thread thread2 = new Thread(my,"正常人");
System.out.println("*****开始抢票**********");
thread0.setPriority(3);
thread0.setPriority(9);
thread0.setPriority(2);
thread0.start();
thread1.start();
thread2.start();
}
实现效果图为: