在了解线程之前,我们来了解下什么是进程?
一个进程就是一个应用程序。在操作系统中每启动一个应用程序就会相应的启动一个进程。例如:千千静听进程,魔兽进程,Word 进程,QQ 进程,JVM 启动对应一个进程。
那什么是线程呢?
线程和进程有什么区别呢?
1.进程A和进程B:内存独立不共享。 2.线程A和线程B:堆内存和方法区内存共享,但是栈内存独立,一个线程一个栈。
在java中,每个栈和每个栈之间互不干扰,各自执行各自的,这就是多线程并发。java中之所以有多线程机制,目的就是为了提高程序的处理效率。
系统引入多进程的作用?
最初的计算机是“单进程的”,计算机只能运行一个应用程序,例如第一台计算机只有DOS 窗口。现代的计算机可以满足我们一边听音乐,一边玩游戏。现代的计算给我们人类感觉:多件事情一起运行。感觉是并行的(错觉) 对于单核的计算机来讲,在某一个时间点上只能做一件事情,但是由于计算机的处理速度很高,多个进程之间完成频繁的切换执行,这个切换速度使人类产生了错觉,人类的错觉是:多个进程在同时运行。 计算机引入多进程的作用:提高 CPU 的使用率。 进程和进程之间的内存独立。
浅谈java 程序的执行原理: java 命令执行会启动 JVM,JVM 的启动表示启动一个应用程序,表示启动了一个进程。该进程会自动启动一个“主线程”,然后主线程负责调用某个类的 main 方法。所以 main 方法的执行是在主线程中执行的。然后通过 main 方法代码的执行可以启动其他的“分支线程”。所以,main 方法结束程序不一定结束,因为其他的分支线程有可能还在执行。
Thread类常用构造方法:
(1)Thread() 分配一个新的 Thread对象。
(2)Thread(String name) 分配一个新的 Thread对象。
Thread类常用方法:
(1)static Thread currentThread() 返回当前正在执行的线程对象的引用
(2)String getName() 返回此线程的名称。
(3)void interrupt() 中断这个线程。
(4)boolean isDaemon() 如果该线程是守护线程。
(5)void setName(String name) 改变该线程的名称等于参数 name。
(6)static void sleep(long millis)当前正在执行的线程休眠(暂停执行)为指定的毫秒数,根据精度和系统定时器和调度的准确性
(7)void start() 导致该线程开始执行;java虚拟机调用这个线程的 run方法。
(8)String toString() 返回此线程的字符串表示形式,包括线程的名称、优先级和线程组。
示例代码(1):
public class ThreadText01 {
public static void main(String[] args) {
//currentThread当前线程对象
//在main方法中,当前线程就是主线程,
Thread tt=Thread.currentThread();
String str=tt.getName();//main
System.out.println(str);
//创建线程对象
MyThread2 t=new MyThread2();
//设置线程名字
t.setName("t1");
//获取线程名字
String s=t.getName();
System.out.println(s);//Thread-0 默认线程名字
MyThread2 t2=new MyThread2();
t2.setName("t2");
String s1=t2.getName();
System.out.println(s1);//Thread-1
//启动线程
t.start();
t2.start();
}
}
class MyThread2 extends Thread{
public void run() {
for (int i = 0; i < 10; i++) {
//获取当前线程t1
//t2线程执行此方法,当前对象就是t2
Thread t=Thread.currentThread();
System.out.println(t.getName()+"线程----->"+i);//t1
try {
//休眠一秒
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果:
main
t1
t2
t2线程----->0
t1线程----->0
t2线程----->1
t1线程----->1
t2线程----->2
t1线程----->2
t2线程----->3
t1线程----->3
t2线程----->4
t1线程----->4
t2线程----->5
t1线程----->5
t2线程----->6
t1线程----->6
t2线程----->7
t1线程----->7
t2线程----->8
t1线程----->8
t2线程----->9
t1线程----->9
Process finished with exit code 0
其实 Thread 对象本身就实现了 Runnable 接口,但一般建议直接使用 Runnable 接口来写多线程程序,因为接口会比类带来更多的好处
示例代码(2):
public class ThreadText02 {
public static void main(String[] args) {
//创建线程
Thread t=new Thread(new MyRunnable());
//启动线程
t.start();
for (int i = 0; i < 10; i++) {
System.out.println("主线程----->"+i);
}
}
}
class MyRunnable implements Runnable{
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("分支线程----->"+i);
}
}
}
运行结果:
主线程----->0
分支线程----->0
主线程----->1
分支线程----->1
主线程----->2
分支线程----->2
主线程----->3
分支线程----->3
主线程----->4
分支线程----->4
主线程----->5
分支线程----->5
主线程----->6
分支线程----->6
主线程----->7
分支线程----->7
分支线程----->8
分支线程----->9
主线程----->8
主线程----->9
这种方式实现的线程可以获取线程的返回值。(效率低) 示例代码:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;//JUC包下的,属于java并发包,老jdk没有,新特性
public class ThreadText14 {
public static void main(String[] args) {
FutureTask task=new FutureTask(new Callable() {
@Override
public Object call() throws Exception {//call方法相当于run方法,但是有返回值
System.out.println("call method begin");
Thread.sleep(1000*2);
int a=100;
int b=200;
return a+b;//(自动装箱)
}
});
Thread ti=new Thread(task);
//开始
ti.start();
//get方法导致当前线程阻塞!!
try {
Object obj=task.get();
System.out.println("线程执行结果--->"+obj);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("hello world");
}
}
运行结果:
call method begin
线程执行结果--->300
hello world
新建:采用 new语句创建完成 就绪:执行 start 后 运行:占用 CPU 时间 阻塞:执行了 wait 语句、执行了 sleep 语句和等待某个对象锁,等待输入的场合 终止:退出 run()方法
三个条件:①多线程并发②有数据共享③共享数据有修改的行为。
异步编程模型:
线程t1和线程t2,各自执行各自的,t1不管t2,t2不管t1,
谁也不需要等谁,这种编程模型叫做:异步编程模型。
其实就是:多线程并发(效率较高。)
异步就是并发。
同步编程模型:
线程t1和线程t2,在线程t1执行的时候,必须等待t2线程执行
结束,或者说在t2线程执行的时候,必须等待t1线程执行结束,
两个线程之间发生了等待关系,这就是同步编程模型。
效率较低。线程排队执行。
同步就是排队。
synchronized有三种写法: ①同步代码块
synchronized(线程共享对象){
同步代码块;
}
线程共享对象必须是多线程共享的数据。
java中,任何对象都有一把锁,是一个标记,让哪个线程排队,让那个线程共享数据
②在实例方法上使用synchronized表示共享对象一定是this并且同步代码块是整个方法体。
③在静态方法上使用synchronized表示找类锁。类锁永远只有1把。
Java中三大变量:
实例变量:在堆中。
静态变量:在方法区。
局部变量:在栈中。
以上三大变量中: 局部变量永远都不会存在线程安全问题。 因为局部变量不共享。(一个线程一个栈。) 局部变量在栈中。所以局部变量永远都不会共享。
实例变量在堆中,堆只有1个。
静态变量在方法区中,方法区只有1个。
堆和方法区都是多线程共享的,所以可能存在线程安全问题。
局部变量+常量:不会有线程安全问题。
成员变量:可能会有线程安全问题。
1.尽量使用局部变量代替“实例变量和静态变量”。 2.如果必须是实例变量,那么可以考虑创建多个对象,这样实例变量的内存就不共享了。 3.如果不能使用局部变量,对象也不能创建多个,这个时候就只能选择synchronized了。线程同步机制。
使用布尔标记: 示例代码(3):
public class ThreadText10 {
public static void main(String[] args) {
MyRunnable4 r = new MyRunnable4();
Thread t = new Thread(r);
t.setName("t");
t.start();
try {
t.sleep(1000*5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//终止线程
//想要什么时候终止,把标记修改为false就终止!
r.run=false;
}
}
class MyRunnable4 implements Runnable {
//打一个布尔标记
Boolean run = true;
@Override
public void run() {
if (run) {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "---->" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} else {
//在这里保存数据!!!
return;
}
}
}
示例代码(4):
public class ThreadText04 {
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object();
Thread t1 = new MyThread01(o1, o2);
Thread t2 = new MyThread02(o1, o2);
t1.start();
t2.start();
}
}
class MyThread01 extends Thread {
Object o1;
Object o2;
public MyThread01(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
@Override
public void run() {
synchronized (o1){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2){
}
}
}
}
class MyThread02 extends Thread {
Object o1;
Object o2;
public MyThread02(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
public void run() {
synchronized (o2){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1){
}
}
}
}
java语言中线程分为两大类:
一类是:用户线程
一类是:守护线程(后台线程)
其中具有代表性的就是:垃圾回收线程(守护线程)。
守护线程:所有的用户线程结束生命周期,守护线程才会结束生命周期,只要有一个用户线程存在,那么守护线程就不会结束,例如 java 中著名的垃圾回收器就是一个守护线程,只有应用程序中所有的线程结束,它才会结束。
守护线程的特点: 一般守护线程是一个死循环,所有的用户线程只要结束, 守护线程自动结束。
示例代码(5):
/*
守护线程!!
*/
public class ThreadText05 {
public static void main(String[] args) {
Thread t = new BakDataThread();
//调用setDaemon方法!
t.setDaemon(true);
t.start();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "--->" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class BakDataThread extends Thread {
int i = 0;
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() + "守护线程备份数据" + (i++));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
定时器的作用:间隔特定的时间,执行特定的程序。 java中其实可以采用多种方式实现:
关于Object类中的wait和notify方法。
Object o = new Object(); o.wait();
表示:
让正在o对象上活动的线程进入等待状态,无期限等待,
直到被唤醒为止。
o.wait();方法的调用,会让“当前线程(正在o对象上
活动的线程)”进入等待状态。
Object o = new Object(); o.notify();
表示:
唤醒正在o对象上等待的线程。
还有一个notifyAll()方法:
这个方法是唤醒o对象上处于等待的所有线程。
wait()和notify()是建立在synchronized(线程同步的基础上!) 示例代码(6):
import java.util.ArrayList;
import java.util.List;
public class ThreadText06 {
public static void main(String[] args) throws Exception {
List list = new ArrayList();
Thread t1 = new Thread(new Producer(list));
Thread t2 = new Thread(new Consumer(list));
t1.setName("生产者线程");
t2.setName("消费者线程");
t1.start();
t2.start();
}
}
//生产者
class Producer implements Runnable {
private List list;
public Producer(List list) {
this.list = list;
}
public void run() {
while (true) {
synchronized (list) {
if (list.size() > 0) {
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//仓库里面有元素。
Object o = new Object();
list.add(o);
System.out.println(Thread.currentThread().getName() + "---->" + o);
//唤醒消费者线程开始消费
list.notifyAll();
}
}
}
}
//消费者
class Consumer implements Runnable {
private List list;
public Consumer(List list) {
this.list = list;
}
public void run() {
while (true) {
synchronized (list) {
if (list.size() == 0) {
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Object o = list.remove(0);
System.out.println(Thread.currentThread().getName() + "----->" + o);
//唤醒生产者线程
list.notifyAll();
}
}![在这里插入图片描述](https://img-blog.csdnimg.cn/20200727171907257.gif)
}
}
今天的分享就到这里啦!!~感谢大家的观看