多线程
1.进程与线程的关系。
-
进程:进程是正在运行的程序的实例。
-
线程:是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
2.线程的基本使用。
java的两种实现方式。
1.继承Thread类
步骤: 1. 编写一个类, 继承Thread类
2. 重写父类的 run 方法 , 在此方法中调用执行的代码 就是在新的线程中执行的代码.
3. 创建此类的对象, 并调用start方法, 来启动此线程 ;
public class text1 {
public static void main(String[] args) {
MyThread mt1 = new MyThread();
//第二条线程
mt1.start();
for(int i=0;i<100;i++){
System.out.println(i);
}
}
static class MyThread extends Thread{
public void run(){
for(int i=0;i<10;i++){
//第一条线程
System.out.println(i);
}
}
}
}
2.实现Runnable接口
- 编写一个类, 实现Runnable接口 , 实现run方法 (run方法的逻辑, 就是在新的线程中执行的)
- 创建上述Runnable接口实现类的对象
- 创建Thread对象, 并在构造方法中, 第二步创建的对象
- 调用Thread对象的start方法,启动线程
public class text2 {
public static void main(String[] args) {
//1. 先创建MyRunnable对象
MyRunnable m1 = new MyRunnable();
//2. 创建Thread 对象,通过构造方法, 传入MyRunnable对象
Thread t1 = new Thread(m1);
//3. 启动线程
//子线程执行
t1.start();
for(int i=0;i<100;i++) {
System.out.println("Main"+i);
}
}
static class MyRunnable implements Runnable{
//主线程的执行
public void run() {
for(int i=0;i<100;i++) {
System.out.println("MyRunnable"+i);
}
}
}
}
3.使用匿名内部类
使用匿名内部类的方式, 快速的编写一个Thread的子类, 然后调用start方法 启动线程
格式是:
new Thread() {
public void run() {
for(int i=0;i<100;i++) {
System.out.println("T一"+i);
}
}
}
.start()
4.两者关系
- Thread类在设计时, 使用到了一个设计模式: 代理设计模式.我们使用第一种方式 和 第二种方式, 其实在内部都会使用代理设计模式,
最终的结果 都是实现了Runnable接口在Thread内部, - 无论是通过第一种方式也好, 第二种方式也好, 最终的效率, 是完全一致的.
- 虽然效率一致, 但是两种方式在使用时 ,更建议使用 第二种方式:
- 原因: 使用继承的方式, 会导致我们编写的类 无法再继承其他的类 . 而使用第二种方式 ,
因为是实现Runnable接口, Java允许多实现 ,所以我们的类还可以继续实现其他的接口, 或者 继承其他的类.
4.sleep方法的运用(休眠作用)和yield的作用
static void sleep(int 毫秒数) ***
此方法执行时 所在的线程睡眠 , 用于降低线程的活跃度。(会让出时间片)
public class text4 {
public static void main(String[] args) {
new Thread() {
public void run() {
//打字机
char[] chars= "锄禾日当午,汗滴禾下土".toCharArray();//一个字一个字的输出
for(int i=0;i<chars.length;i++){
System.out.print(chars[i]);
try {
Thread.sleep(1000);//每隔一秒打一个字
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
static void yield();
此方法执行时所在的线程, 让出当前时间片,给其他的资源用. 用于降低线程的活跃度。
void start()
启动此线程所表示的线程对象, 并在新的线程中执行run方法,如果不启用start的方法的话线程run是不会执行的。
void setName(String name) | String getName();
设置/获取 线程名称; 线程拥有默认的名称
stop()
停止线程(已过时 , 不建议使用, 因为会造成固有的不安全性);
线程的生命周期
新建线程——>就绪——>运行——>死亡
运行到死亡期间还会有一个阻塞,形成阻塞有多种方式{等待,睡眠,锁…}然后再到就绪。
线程的同步异步。
同步就是排队,线程安全,效率低下。
异步就是并排,线程不安全,效率高。
两者都是通过锁来实现的
同步:多线程执行是会有安全问题,解决的方法就是排队
java实现同步的原理就是加锁,当执行A线程是会给代码上锁,只有当A线程执行完了A线程才会将锁解开,允许其他线程执行。
两者的那个好?
有就有存在的道理 各有优劣.如果非说哪个好的话就是同步代码块。
text5:线程的同步方法一:同步代码块.(上锁)
text6:线程的同步方法二:同步方法
public class text5 {
public static void main(String[] args) {
Thread t1=new Thread(){
public void run() {
print1();
}
};
Thread t2=new Thread(){
public void run() {
print2();
}
};
t1.start();
t2.start();
}
static Object ob1= new Object();
static Object ob2= new Object();
public static void print1(){
//同步代码快:此代码块在执行的时候是排队执行的
//():传递的是锁的对象
//两个线程如果用两个不同的锁的话是不会起到排队的效果,必须是用同一个锁
//同一个锁可以放在两个代码块中
synchronized (ob1) {
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 5; j++) {
System.out.println(Thread.currentThread().getName()+"-------"+i+j);
}
}
}
}
public static void print2(){
//同步代码快:此代码块在执行的时候是排队执行的
//():传递的是锁的对象
synchronized (ob1) {
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 5; j++) {
System.out.println(Thread.currentThread().getName()+"-------"+i+j);
}
}
}
}
}
public class text6 {
public static void main(String[] args) {
Thread t1 = new Thread(){
@Override
public void run() {
print();
}
};
Thread t2 = new Thread(){
@Override
public void run() {
print2();
}
};
t1.start();
t2.start();
//同步方法
//非静态同步方法的锁对象是:this
//静态同步方法的锁对象是;类名.class. 可以new一个对象来调用,也可以个new一个对象()但是不会排队
}
public synchronized static void print(){
for(int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+i);
}
}
public synchronized static void print2(){
for(int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+i);
}
}
}